LLM Token Contract Documentation
The LLM Token Contract sets up a scenario to manage token transactions between users, ensuring that each transaction is valid and updating balances accordingly. The contract uses the Equivalence Principle to maintain consistency and accuracy.
# { "Depends": "py-genlayer:test" }
import json
from genlayer import *
@gl.contract
class LlmErc20:
balances: TreeMap[Address, u256]
def __init__(self, total_supply: int) -> None:
self.balances[gl.message.sender_account] = u256(total_supply)
@gl.public.write
def transfer(self, amount: int, to_address: str) -> None:
prompt = f"""
You keep track of transactions between users and their balance in coins.
The current balance for all users in JSON format is:
{json.dumps(self.get_balances())}
The transaction to compute is: {{
sender: "{gl.message.sender_account.as_hex}",
recipient: "{Address(to_address).as_hex}",
amount: {amount},
}}
For every transaction, validate that the user sending the Coins has
enough balance. If any transaction is invalid, it shouldn't be processed.
Update the balances based on the valid transactions only.
Given the current balance in JSON format and the transaction provided,
please provide the result of your calculation with the following format:
{{
"transaction_success": bool, // Whether the transaction was successful
"transaction_error": str, // Empty if transaction is successful
"updated_balances": object<str, int> // Updated balances after the transaction
}}
It is mandatory that you respond only using the JSON format above,
nothing else. Don't include any other words or characters,
your output must be only JSON without any formatting prefix or suffix.
This result should be perfectly parsable by a JSON parser without errors."""
print(prompt)
def run():
res = gl.exec_prompt(prompt)
res = res.replace("```json", "").replace("```", "")
return res
final_result = gl.eq_principle_prompt_comparative(
run,
"""The new_balance of the sender should have decreased
in the amount sent and the new_balance of the receiver should have
increased by the amount sent. Also, the total sum of all balances
should have remain the same before and after the transaction""",
)
print("final_result: ", final_result)
result_json = json.loads(final_result)
for k, v in result_json["updated_balances"].items():
self.balances[Address(k)] = v
@gl.public.view
def get_balances(self) -> dict[str, int]:
return {k.as_hex: v for k, v in self.balances.items()}
@gl.public.view
def get_balance_of(self, address: str) -> int:
return self.balances.get(Address(address), 0)
Code Explanation
-
Initialization: The
LlmErc20
class initializes the contract with a total supply of tokens assigned to the contract creator's address.[contract_runner["from_address"]]
represents the address that called the contract function, helping to track and validate transactions. -
Prompts: Next, we construct prompts to ensure that the LLM can accurately validate and process transactions based on the provided user balances and transaction details (sender, recipient, and amount). This prompt is sent to the language model (LLM) for processing and validation.
-
EquivalencePrinciple: The Equivalence Principle validates that the sender's balance decreases by the amount sent, the recipient's balance increases by the same amount, and the total sum of all balances remains unchanged.
-
JSON Response: The LLM processes the prompt and returns a JSON response indicating the transaction status and updated balances.
-
Balance Update: The contract updates the balances based on the LLM response.
-
Balance Retrieval Methods:
get_balances()
: Returns the balances of all users.get_balance_of(address)
: Returns the balance of a specific user given their address.
You can view this code on our GitHub (opens in a new tab).
Deploying the Contract
To deploy the ERC-20 Token contract, you need to initialize the contract state correctly. This setup will determine how the contract manages token transactions.
- Set Total Supply: Provide the total supply of tokens. The
total_supply
constructor parameter is detected from the code. For example, you might settotal_supply
to 1000. - Deploy the Contract: Once the total supply is set, deploy the contract to make it ready to interact and process token transactions.
Checking the Contract State
After deploying the contract, its address is displayed and you can check its state in the Read Methods section. Use the get_balances()
function to see the balances of all users and the get_balance_of(address)
function to see the balance of a specific user.
Executing Transactions
To interact with the deployed contract, go to the Write Methods section. Here, you can call the transfer
method to process a token transfer. This triggers the contract's logic to validate the transaction and update balances based on the Equivalence Principle criteria defined.
Analyzing the Contract's Decisions
When the transfer
method is executed:
- The LLM processes the transaction details.
- It validates the transaction according to the Equivalence Principle defined in the code.
- It returns a JSON response that includes the transaction status and updated balances.
Handling Different Scenarios
- Valid Transactions: If the sender has enough balance, the transaction is successful. The sender's balance decreases by the amount sent, and the recipient's balance increases by the same amount. The total sum of all balances remains unchanged.
- Invalid Transactions: If the sender does not have enough balance, the transaction fails. The balances remain unchanged, and the JSON response includes an error message.
You can view the logs to see detailed information about the contract interaction.