Skip to content

Commit

Permalink
ZIL-5536: New ERC20ProxyForZRC2 contract example (#351)
Browse files Browse the repository at this point in the history
* ERC2ProxyForZRC2

* Update readme

* update bazelignore
  • Loading branch information
WuBruno authored Jan 22, 2024
1 parent 5e38abe commit c013b60
Show file tree
Hide file tree
Showing 12 changed files with 2,886 additions and 144 deletions.
3 changes: 2 additions & 1 deletion .bazelignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ zilliqa/js/subscriptions/node_modules
zilliqa/js/util/node_modules
zilliqa/js/zilliqa/node_modules
examples/zilliqa-js/latest-block/node_modules
examples/zilliqa-js/react-zilliqa-js/node_modules
examples/zilliqa-js/react-zilliqa-js/node_modules
contracts/experimental/ERC20ProxyForZRC2/node_modules
1 change: 1 addition & 0 deletions contracts/experimental/ERC20ProxyForZRC2/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PRIVATE-KEY=
14 changes: 14 additions & 0 deletions contracts/experimental/ERC20ProxyForZRC2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
node_modules
.env

# Hardhat files
/cache
/artifacts

# TypeChain files
/typechain
/typechain-types

# solidity-coverage files
/coverage
/coverage.json
21 changes: 21 additions & 0 deletions contracts/experimental/ERC20ProxyForZRC2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ERC20ProxyForZRC2 Contract

This is the contract to deploy a ERC20Proxy for a ZRC2 contract living in the scilla environment. It leverages the precompiles available in Zilliqa to interoperate between the 2 environments.

Make sure to specify the `zrc2_address` on the deployment file for the ERC20Proxy to be correctly deployed. This allows EVM to execute all desired functions on the ZRC2 as if it were a ERC20. Implementing IERC20 means that all existing DApps and wallets should be compatible with this token.

Make sure to also copy `.env.example` into `.env` and fill in the necessarily variables. Also ensure that `pnpm install` to install any necessary dependencies

The following are the deployment commands:

- Zilliqa Mainnet

```shell
pnpm exec hardhat run scripts/deploy.ts --network zq
```

- Zilliqa Testnet

```shell
pnpm exec hardhat run scripts/deploy.ts --network zq-testnet
```
238 changes: 238 additions & 0 deletions contracts/experimental/ERC20ProxyForZRC2/contracts/ScillaConnector.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

library ScillaConnector {
uint private constant CALL_SCILLA_WITH_THE_SAME_SENDER = 1;
uint private constant SCILLA_CALL_PRECOMPILE_ADDRESS = 0x5a494c53;
uint private constant SCILLA_STATE_READ_PRECOMPILE_ADDRESS = 0x5a494c92;

/**
* @dev Calls a ZRC2 contract function with two arguments
* @param target The address of the ZRC2 contract
* @param tran_name The name of the function to call
* @param arg1 The first argument to the function
* @param arg2 The second argument to the function
*/
function call(
address target,
string memory tran_name,
address arg1,
uint128 arg2
) internal {
bytes memory encodedArgs = abi.encode(
target,
tran_name,
CALL_SCILLA_WITH_THE_SAME_SENDER,
arg1,
arg2
);
uint256 argsLength = encodedArgs.length;

assembly {
let alwaysSuccessForThisPrecompile := call(
21000,
SCILLA_CALL_PRECOMPILE_ADDRESS,
0,
add(encodedArgs, 0x20),
argsLength,
0x20,
0
)
}
}

/**
* @dev Calls a ZRC2 contract function with three arguments
* @param target The address of the ZRC2 contract
* @param tran_name The name of the function to call on the ZRC2 contract
* @param arg1 The first argument to the function
* @param arg2 The second argument to the function
* @param arg3 The third argument to the function
*/
function call(
address target,
string memory tran_name,
address arg1,
address arg2,
uint128 arg3
) internal {
bytes memory encodedArgs = abi.encode(
target,
tran_name,
CALL_SCILLA_WITH_THE_SAME_SENDER,
arg1,
arg2,
arg3
);
uint256 argsLength = encodedArgs.length;

assembly {
let alwaysSuccessForThisPrecompile := call(
21000,
SCILLA_CALL_PRECOMPILE_ADDRESS,
0,
add(encodedArgs, 0x20),
argsLength,
0x20,
0
)
}
}

/**
* @dev Reads a 128 bit integer from a ZRC2 contract
* @param target The address of the ZRC2 contract
* @param variable_name The name of the variable to read from the ZRC2 contract
* @return The value of the variable
*/
function readUint128(
address target,
string memory variable_name
) internal view returns (uint128) {
bytes memory encodedArgs = abi.encode(target, variable_name);
uint256 argsLength = encodedArgs.length;
bytes memory output = new bytes(36);

assembly {
let alwaysSuccessForThisPrecompile := staticcall(
21000,
SCILLA_STATE_READ_PRECOMPILE_ADDRESS,
add(encodedArgs, 0x20),
argsLength,
add(output, 0x20),
32
)
}

return abi.decode(output, (uint128));
}

/**
* @dev Reads a 32 bit integer from a ZRC2 contract
* @param target The address of the ZRC2 contract
* @param variable_name The name of the variable to read from the ZRC2 contract
* @return The value of the variable
*/
function readUint32(
address target,
string memory variable_name
) internal view returns (uint32) {
bytes memory encodedArgs = abi.encode(target, variable_name);
uint256 argsLength = encodedArgs.length;
bytes memory output = new bytes(36);

assembly {
let alwaysSuccessForThisPrecompile := staticcall(
21000,
SCILLA_STATE_READ_PRECOMPILE_ADDRESS,
add(encodedArgs, 0x20),
argsLength,
add(output, 0x20),
32
)
}

return abi.decode(output, (uint32));
}

/**
* @dev Reads a string from a ZRC2 contract
* @param target The address of the ZRC2 contract
* @param variable_name The name of the variable to read from the ZRC2 contract
* @return retVal The value of the variable
*/
function readString(
address target,
string memory variable_name
) internal view returns (string memory retVal) {
bytes memory encodedArgs = abi.encode(target, variable_name);
uint256 argsLength = encodedArgs.length;
bool success;
bytes memory output = new bytes(128);
uint256 output_len = output.length - 4;
assembly {
success := staticcall(
21000,
SCILLA_STATE_READ_PRECOMPILE_ADDRESS,
add(encodedArgs, 0x20),
argsLength,
add(output, 0x20),
output_len
)
}
require(success);

(retVal) = abi.decode(output, (string));
return retVal;
}

/**
* @dev Reads a 128 bit integer from a map in a ZRC2 contract
* @param variable_name The name of the map in the ZRC2 contract
* @param addressMapKey The key to the map
* @return The value associated with the key in the map
*/
function readMapUint128(
address target,
string memory variable_name,
address addressMapKey
) internal view returns (uint128) {
bytes memory encodedArgs = abi.encode(
target,
variable_name,
addressMapKey
);
uint256 argsLength = encodedArgs.length;
bytes memory output = new bytes(36);

assembly {
let alwaysSuccessForThisPrecompile := staticcall(
21000,
SCILLA_STATE_READ_PRECOMPILE_ADDRESS,
add(encodedArgs, 0x20),
argsLength,
add(output, 0x20),
32
)
}

return abi.decode(output, (uint128));
}

/**
* @dev Reads a 128 bit integer from a nested map in a ZRC2 contract
* @param target The address of the ZRC2 contract
* @param variable_name The name of the map in the ZRC2 contract
* @param firstMapKey The first key to the map
* @param secondMapKey The second key to the map
* @return The value associated with the keys in the map
*/
function readNestedMapUint128(
address target,
string memory variable_name,
address firstMapKey,
address secondMapKey
) internal view returns (uint128) {
bytes memory encodedArgs = abi.encode(
target,
variable_name,
firstMapKey,
secondMapKey
);
uint256 argsLength = encodedArgs.length;
bytes memory output = new bytes(36);

assembly {
let alwaysSuccessForThisPrecompile := staticcall(
21000,
SCILLA_STATE_READ_PRECOMPILE_ADDRESS,
add(encodedArgs, 0x20),
argsLength,
add(output, 0x20),
32
)
}

return abi.decode(output, (uint128));
}
}
Loading

0 comments on commit c013b60

Please sign in to comment.