Skip to content

Commit

Permalink
clarify and expand COA docs (#901)
Browse files Browse the repository at this point in the history
  • Loading branch information
sisyphusSmiling authored Sep 17, 2024
1 parent 8538cca commit 8ab0e1d
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 16 deletions.
16 changes: 14 additions & 2 deletions docs/evm/cadence/direct-calls.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Once you have access to an `EVMAddress`, you can query various pieces of state i
- `nonce() UInt64` retrieves the nonce associated with the address.
- `code(): [UInt8]` fetches the code at the address; it returns the smart contract code if applicable, and is empty otherwise.

```
```cadence
import EVM from <ServiceAddress>
access(all)
Expand All @@ -39,11 +39,23 @@ fun main(bytes: [UInt8; 20]): EVM.Balance {
}
```

Alternatively, you can use the EVM contract's native deserialization to access the balance provided a hex string representing the address:

```cadence
import EVM from <ServiceAddress>
access(all)
fun main(addressHex: String): UFix64 {
let addr = EVM.addressFromString(addressHex)
return addr.balance().inFLOW()
}
```

### Sending Transactions to Flow EVM

To send transactions to Flow EVM, use the `run` function which executes RLP-encoded transactions. RLP (Recursive Length Prefix) encoding is used to efficiently encode data into a byte-array format, suitable for Ethereum-based environments. Here's an example of wrapping and sending a transaction:

```
```cadence
import EVM from <ServiceAddress>
transaction(rlpEncodedTransaction: [UInt8], coinbaseBytes: [UInt8; 20]) {
Expand Down
66 changes: 52 additions & 14 deletions docs/evm/cadence/interacting-with-coa.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ fun main(address: Address): EVM.EVMAddress {
}
```

If you'd prefer the hex representation of the address, you instead return using the `EVMAddress.toString()` function:

```cadence
return coa.address().toString()
```

The above will return the EVM address as a string; however note that Cadence does not prefix hex strings with `0x`.

## Getting the Flow Balance of a COA

Like any other Flow EVM or Cadence account, COAs possess a balance of FLOW tokens. To get the current balance of our COA, we can use the COA's `balance` function. It will return a `EVM.Balance` struct for the account - these are used to represent balances within Flow EVM.
Expand All @@ -172,6 +180,21 @@ fun main(address: Address): EVM.Balance {
}
```

You can also easily get the `UFix64` FLOW balance of any EVM address with this script:

```cadence
import "EVM"
access(all)
fun main(addressHex: String): UFix64 {
let addr = EVM.addressFromString(addressHex)
return addr.balance().inFLOW()
}
```

The above script is helpful if you already know the COA address and can provide the hex representation directly.


## Depositing and Withdrawing Flow Tokens

Tokens can be seamlessly transferred between the Flow EVM and Cadence environment using the `deposit` and `withdraw` functions provided by the COA resource. Anybody with a valid reference to a COA may deposit Flow tokens into a it, however only someone with the `Owner` or `Withdraw` entitlements can withdraw tokens.
Expand All @@ -191,7 +214,7 @@ transaction(amount: UFix64) {
let coa: &EVM.CadenceOwnedAccount
let sentVault: @FlowToken.Vault
prepare(signer: auth(Capabilities, Storage) &Account) {
prepare(signer: auth(BorrowValue) &Account) {
// Borrow the public capability to the COA from the desired account
// This script could be modified to deposit into any account with a `EVM.CadenceOwnedAccount` capability
self.coa = signer.capabilities.borrow<&EVM.CadenceOwnedAccount>(
Expand Down Expand Up @@ -233,7 +256,7 @@ transaction(amount: UFix64) {
let sentVault: @FlowToken.Vault
let receiver: &{FungibleToken.Receiver}
prepare(signer: auth(Storage, EVM.Withdraw) &Account) {
prepare(signer: auth(BorrowValue) &Account) {
// Borrow a reference to the COA from the storage location we saved it to with the `EVM.Withdraw` entitlement
let coa = signer.storage.borrow<auth(EVM.Withdraw) &EVM.CadenceOwnedAccount>(
from: /storage/evm
Expand Down Expand Up @@ -266,30 +289,40 @@ This is a basic example which only transfers tokens between a single user's COA

To interact with smart contracts on the EVM, you can use the `call` function provided by the COA resource. This function takes the EVM address of the contract you want to call, the data you want to send, the gas limit, and the value you want to send. It will return a `EVM.Result` struct with the result of the call - you will need to handle this result in your Cadence code.

This transaction will call a contract at a given EVM address with a hardcoded data payload, gas limit, and value using the signer's COA:
This transaction will use the signer's COA to call a contract method with the defined signature and args at a given EVM address, executing with the provided gas limit and value:

```cadence
import "EVM"
transaction() {
transaction(evmContractHex: String, signature: String, args: [AnyStruct], gasLimit: UInt64, value: UFix64) {
let coa: auth(EVM.Call) &EVM.CadenceOwnedAccount
prepare(signer: auth(Storage) &Account) {
prepare(signer: auth(BorrowValue) &Account) {
// Borrow an entitled reference to the COA from the storage location we saved it to
self.coa = signer.storage.borrow<auth(EVM.Call) &EVM.CadenceOwnedAccount>(
from: /storage/evm
) ?? panic("Could not borrow reference to the COA")
}
execute {
// Deserialize the EVM address from the hex string
let contractAddress = EVM.addressFromString(evmContractHex)
// Construct the calldata from the signature and arguments
let calldata = EVM.encodeABIWithSignature(
signature,
args
)
// Define the value as EVM.Balance struct
let value = EVM.Balance(attoflow: 0)
value.setFLOW(flow: value)
// Call the contract at the given EVM address with the given data, gas limit, and value
// These values could be configured through the transaction arguments or other means
// however, for simplicity, we will hardcode them here
let result: EVM.Result = coa.call(
to: 0x1234567890123456789012345678901234567890, // INSERT EVM ADDRESS HERE
data: [0x01, 0x02, 0x03], // INSERT DATA HERE
gasLimit: 15000000, // INSERT GAS LIMIT HERE (attoflow)
value: 0.0 // INSERT VALUE HERE
to: contractAddress,
data: calldata,
gasLimit: gasLimit,
value: value
)
// Revert the transaction if the call was not successful
Expand All @@ -302,6 +335,11 @@ transaction() {
}
}
```
:::info
Notice that the calldata is encoded in the scope of the transaction. While developers can encode the calldata outside the scope of the transaction and pass the encoded data as an argument, doing so compromises the human-readability of Cadence transactions.

It's encouraged to either define transactions for each COA call and encoded the hardcoded EVM signature and arguments, or to pass in the human-readable arguments and signature and encode the calldata within the transaction. This ensures a more interpretable and therefore transparent transaction.
:::

### Deploying a Contract to Flow EVM

Expand All @@ -312,21 +350,21 @@ This transaction will deploy a contract with the given code using the signer's C
```cadence
import "EVM"
transaction(code: String) {
transaction(bytecode: String) {
let coa: auth(EVM.Deploy) &EVM.CadenceOwnedAccount
prepare(signer: auth(Storage) &Account) {
prepare(signer: auth(BorrowValue) &Account) {
// Borrow an entitled reference to the COA from the storage location we saved it to
self.coa = signer.storage.borrow<auth(EVM.Deploy) &EVM.CadenceOwnedAccount>(
from: /storage/evm
) ?? panic("Could not borrow reference to the COA")
}
execute {
// Deploy the contract with the given code, gas limit, and value
// Deploy the contract with the given compiled bytecode, gas limit, and value
self.coa.deploy(
code: code.decodeHex(),
gasLimit: 15000000, // can be adjusted as needed, maxed for simplicity
code: bytecode.decodeHex(),
gasLimit: 15_000_000, // can be adjusted as needed, hard coded here for simplicity
value: EVM.Balance(attoflow: 0)
)
}
Expand Down

0 comments on commit 8ab0e1d

Please sign in to comment.