Skip to content

Instantly share code, notes, and snippets.

@NoahMarconi
Created June 27, 2018 14:38
Show Gist options
  • Save NoahMarconi/be4213f8147dea60a1ab30db86760828 to your computer and use it in GitHub Desktop.
Save NoahMarconi/be4213f8147dea60a1ab30db86760828 to your computer and use it in GitHub Desktop.
ABI Encoder Examples
pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
contract ABIExample {
enum Permission { ReadOnly, Write, Admin }
struct User {
uint256 id;
Permission permission;
}
User[] users;
constructor() public {
users.push(User(uint256(111), Permission.Admin));
users.push(User(uint256(222), Permission.Write));
users.push(User(uint256(333), Permission.ReadOnly));
}
function getUsers() public constant returns (User[]) {
return users;
}
function getUsersBytes() public constant returns (bytes) {
User[] memory allUsers = users;
return abi.encode(allUsers);
}
function set(User newUser) public {
users.push(newUser);
}
}
pragma solidity ^0.4.24;
contract Caller {
function callExternal(address _to, bytes _data)
public
returns (bool success)
{
assembly {
success := call(not(0), _to, 0, add(_data, 0x20), mload(_data), 0, 0)
}
}
}
// The following example is meant to be a simple self contained example.
//
// To run, paste into a node console and replace `CODE HERE` with your
// solidity code.
const ethers = require('ethers');
const solc = require('solc');
const ganache = require("ganache-cli");
// Provider / Signer setup.
const providers = ethers.providers;
const web3Provider = new providers.Web3Provider(ganache.provider({ gasLimit: 6e6 }));
const signer = web3Provider.getSigner();
// Compile the code snippets.
let contractCode = `ABI EXAMPLE CONTRACT CODE HERE`; // Backticks for multi-line strings.
let callerContractCode = `CALLER CONTRACT CODE HERE`; // Backticks for multi-line strings.
let compiledContract = solc.compile(contractCode);
let callerCompiledContract = solc.compile(callerContractCode);
let { interface, bytecode } = compiledContract.contracts[':ABIExample'];
let deployTransaction = ethers.Contract.getDeployTransaction('0x' + bytecode, interface);
let sendPromise = signer.sendTransaction(Object.assign(deployTransaction, { gas: 6e6 }));
// Set up Caller contract deployment.
let callerInterface = callerCompiledContract.contracts[':Caller'].interface;
let callerBytecode = callerCompiledContract.contracts[':Caller'].bytecode;
let deployCallerTransaction = ethers.Contract.getDeployTransaction('0x' + callerBytecode, callerInterface);
let callerSendPromise = signer.sendTransaction(Object.assign(deployCallerTransaction, { gas: 6e6 }));
// The following example is meant to be a simple self contained example.
//
// To run, paste into a node console after having run 1_compile.js.
// Set up ABIExample contract deployment.
let ABIExample;
let userArray;
let userBytesArray;
sendPromise.then(res => {
web3Provider.getTransactionReceipt(res.hash).then(res => {
// JS contract interface.
ABIExample = new ethers.Contract(res.contractAddress, interface, web3Provider);
// Log result of calling the contract's `getUsers()` function.
ABIExample.getUsers().then(res => {
userArray = res;
console.log('\n\n\n\nUser Array: ');
console.log(userArray);
});
// Log result of calling the contract's `getUsersBytes()` function.
ABIExample.getUsersBytes().then(res => {
userBytesArray = res;
console.log('\n\n\n\n\User Bytes Array: ');
console.log(userBytesArray);
});
});
});
// The following example is meant to be a simple self contained example.
//
// To run, paste into a node console after having run 2_read.js.
let usersArrayType = ["tuple(uint256,uint8)[]"];
let decodedData = ethers.utils.AbiCoder.defaultCoder.decode(usersArrayType, userBytesArray);
console.log(decodedData);
// Or to print nicer decodedData[0].forEach(x => console.log(`id -> ${x[0]} permission -> ${x[1]}`));
// Or if you have the abi the following would work as well.
usersArrayType = [{"components":[{"name":"id","type":"uint256"},{"name":"permission","type":"uint8"}],"name":"","type":"tuple[]"}]
decodedData = ethers.utils.AbiCoder.defaultCoder.decode(usersArrayType, userBytesArray);
console.log(decodedData);
// The following example is meant to be a simple self contained example.
//
// To run, paste into a node console after having run 2_read.js.
sendPromise.then(res => {
web3Provider.getTransactionReceipt(res.hash).then(res => {
// New JS contract interface.
ABIExample = new ethers.Contract(res.contractAddress, interface, signer);
// Update state with User(444, Permission.Admin).
ABIExample.set({ id: 444, permission: 2 }, { gasLimit: 2e5 }).then(function(transaction) {
// Get the users array again to see the new `User` persisted.
ABIExample.getUsers().then(res => {
// Log the result.
userArray = res;
console.log('\n\n\n\n\User Bytes Array with Additional User: ');
console.log(userArray);
});
});
});
});
// The following example is meant to be a simple self contained example.
//
// To run, paste into a node console after having run 2_read.js.
let userType = ["tuple(uint256,uint8)"];
let inputData = [ // AbiCoder wraps all of the data.
[ // User tuple.
555, // uint256 representing `id`.
0, // uint256 representing `permission`.
]
];
// Missing function sig prefix.
let encodedData = ethers.utils.AbiCoder.defaultCoder.encode(userType, inputData);
// Includes prefix using ethers.js and our contract interface.
encodedData = ABIExample.interface.functions.set({ id: 555, permission: 0 }).data;
let Caller;
callerSendPromise.then(res => {
web3Provider.getTransactionReceipt(res.hash).then(res => {
// New JS contract interface.
Caller = new ethers.Contract(res.contractAddress, callerInterface, signer);
// Update state with User(555, Permission.ReadOnly).
Caller.callExternal(ABIExample.address, encodedData, { gasLimit: 2e5 }).then(res => {
// Get the users array again to see the new `User` persisted.
ABIExample.getUsers().then(res => {
// Log result.
userArray = res;
console.log('\n\n\n\n\User Bytes Array with Additional User: ');
console.log(userArray);
});
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment