Skip to content

Commit

Permalink
[EVM] Prevent registering pointers to pointers (sei-protocol#1638)
Browse files Browse the repository at this point in the history
* small test refactor

* add coverage for keeper logic

* cleanup

* add register step to cw tests

* add erc tests

* update code id for error

* goimports and cleanup
  • Loading branch information
stevenlanders authored May 8, 2024
1 parent 9d9d7a6 commit 8b1f76d
Show file tree
Hide file tree
Showing 7 changed files with 411 additions and 130 deletions.
20 changes: 14 additions & 6 deletions contracts/test/CW20toERC20PointerTest.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
const {fundAddress, storeWasm, instantiateWasm, getSeiAddress, getAdmin, queryWasm, executeWasm, deployEvmContract, setupSigners,
getEvmAddress
const {getAdmin, queryWasm, executeWasm, deployEvmContract, setupSigners, deployErc20PointerForCw20, deployWasm, WASM,
registerPointerForCw20
} = require("./lib")
const { expect } = require("chai");
const {getAdminAddress} = require("@openzeppelin/upgrades-core");

const CW20_POINTER_WASM = "../example/cosmwasm/cw20/artifacts/cwerc20.wasm";
describe("CW20 to ERC20 Pointer", function () {
let accounts;
let testToken;
Expand All @@ -29,8 +27,7 @@ describe("CW20 to ERC20 Pointer", function () {
admin = await getAdmin()
await setBalance(admin.evmAddress, 1000000000000)

const codeId = await storeWasm(CW20_POINTER_WASM)
cw20Pointer = await instantiateWasm(codeId, accounts[0].seiAddress, "cw20-erc20", {erc20_address: tokenAddr })
cw20Pointer = await registerPointerForCw20(tokenAddr)
})

async function assertUnsupported(addr, operation, args) {
Expand All @@ -43,6 +40,17 @@ describe("CW20 to ERC20 Pointer", function () {
}
}

describe("validation", function(){
it("should not allow a pointer to the pointer", async function(){
try {
await deployErc20PointerForCw20(hre.ethers.provider, cw20Pointer, 5)
expect.fail(`Expected to be prevented from creating a pointer`);
} catch(e){
expect(e.message).to.include("contract deployment failed");
}
})
})

describe("query", function(){
it("should return token_info", async function(){
const result = await queryWasm(cw20Pointer, "token_info", {})
Expand Down
25 changes: 16 additions & 9 deletions contracts/test/CW721toERC721PointerTest.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const {setupSigners, deployEvmContract, getAdmin, deployWasm, executeWasm, queryWasm} = require("./lib");
const {setupSigners, deployEvmContract, getAdmin, deployWasm, executeWasm, queryWasm, deployErc20PointerForCw20,
deployErc721PointerForCw721, WASM, registerPointerForCw721
} = require("./lib");
const {expect} = require("chai");

const CW721_POINTER_WASM = "../example/cosmwasm/cw721/artifacts/cwerc721.wasm";

describe("CW721 to ERC721 Pointer", function () {
let accounts;
let erc721;
Expand All @@ -13,11 +13,8 @@ describe("CW721 to ERC721 Pointer", function () {
accounts = await setupSigners(await hre.ethers.getSigners())
erc721 = await deployEvmContract("MyNFT")
admin = await getAdmin()
pointer = await deployWasm(CW721_POINTER_WASM,
accounts[0].seiAddress,
"cw721-erc721",
{erc721_address: await erc721.getAddress() }
)

pointer = await registerPointerForCw721(await erc721.getAddress())

await (await erc721.mint(accounts[0].evmAddress, 1)).wait()
await (await erc721.mint(accounts[1].evmAddress, 2)).wait()
Expand All @@ -27,8 +24,18 @@ describe("CW721 to ERC721 Pointer", function () {
await (await erc721.setApprovalForAll(admin.evmAddress, true)).wait();
})

describe("query", function(){
describe("validation", function(){
it("should not allow a pointer to the pointer", async function(){
try {
await deployErc721PointerForCw721(hre.ethers.provider, pointer, 5)
expect.fail(`Expected to be prevented from creating a pointer`);
} catch(e){
expect(e.message).to.include("contract deployment failed");
}
})
})

describe("query", function(){
it("should query the owner of a token", async function () {
const result = await queryWasm(pointer, "owner_of", { token_id: "1" });
expect(result).to.deep.equal({data:{
Expand Down
34 changes: 15 additions & 19 deletions contracts/test/ERC20toCW20PointerTest.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
const {setupSigners, deployErc20PointerForCw20, getAdmin, storeWasm, instantiateWasm} = require("./lib");
const {setupSigners, deployErc20PointerForCw20, getAdmin, deployWasm, WASM, ABI, registerPointerForCw20
} = require("./lib");
const {expect} = require("chai");

const CW20_BASE_WASM_LOCATION = "../contracts/wasm/cw20_base.wasm";

const erc20Abi = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function decimals() view returns (uint8)",
"function totalSupply() view returns (uint256)",
"function balanceOf(address owner) view returns (uint256 balance)",
"function transfer(address to, uint amount) returns (bool)",
"function allowance(address owner, address spender) view returns (uint256)",
"function approve(address spender, uint256 value) returns (bool)",
"function transferFrom(address from, address to, uint value) returns (bool)"
];


describe("ERC20 to CW20 Pointer", function () {
let accounts;
let pointer;
Expand All @@ -26,8 +12,7 @@ describe("ERC20 to CW20 Pointer", function () {
accounts = await setupSigners(await hre.ethers.getSigners())
admin = await getAdmin()

const codeId = await storeWasm(CW20_BASE_WASM_LOCATION)
cw20Address = await instantiateWasm(codeId, accounts[0].seiAddress, "cw20", {
cw20Address = await deployWasm(WASM.CW20, accounts[0].seiAddress, "cw20", {
name: "Test",
symbol: "TEST",
decimals: 6,
Expand All @@ -43,10 +28,21 @@ describe("ERC20 to CW20 Pointer", function () {

// deploy TestToken
const pointerAddr = await deployErc20PointerForCw20(hre.ethers.provider, cw20Address)
const contract = new hre.ethers.Contract(pointerAddr, erc20Abi, hre.ethers.provider);
const contract = new hre.ethers.Contract(pointerAddr, ABI.ERC20, hre.ethers.provider);
pointer = contract.connect(accounts[0].signer)
})

describe("validation", function(){
it("should not allow a pointer to the pointer", async function(){
try {
await registerPointerForCw20(await pointer.getAddress())
expect.fail(`Expected to be prevented from creating a pointer`);
} catch(e){
expect(e.message).to.include("contract deployment failed");
}
})
})

describe("read", function(){
it("get name", async function () {
const name = await pointer.name();
Expand Down
36 changes: 15 additions & 21 deletions contracts/test/ERC721toCW721PointerTest.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,10 @@
const {setupSigners, deployErc721PointerForCw721, getAdmin, deployWasm, executeWasm} = require("./lib");
const {setupSigners, deployErc721PointerForCw721, getAdmin, deployWasm, executeWasm, ABI, registerPointerForCw20,
registerPointerForCw721
} = require("./lib");
const {expect} = require("chai");

const CW721_BASE_WASM_LOCATION = "../contracts/wasm/cw721_base.wasm";

const erc721Abi = [
"event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId)",
"event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)",
"event ApprovalForAll(address indexed owner, address indexed operator, bool approved)",
"function name() view returns (string)",
"function symbol() view returns (string)",
"function totalSupply() view returns (uint256)",
"function tokenURI(uint256 tokenId) view returns (string)",
"function balanceOf(address owner) view returns (uint256 balance)",
"function ownerOf(uint256 tokenId) view returns (address owner)",
"function getApproved(uint256 tokenId) view returns (address operator)",
"function isApprovedForAll(address owner, address operator) view returns (bool)",
"function approve(address to, uint256 tokenId) returns (bool)",
"function setApprovalForAll(address operator, bool _approved) returns (bool)",
"function transferFrom(address from, address to, uint256 tokenId) returns (bool)",
"function safeTransferFrom(address from, address to, uint256 tokenId) returns (bool)",
"function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) returns (bool)"
];

describe("ERC721 to CW721 Pointer", function () {
let accounts;
let pointerAcc0;
Expand All @@ -44,11 +27,22 @@ describe("ERC721 to CW721 Pointer", function () {
await executeWasm(cw721Address, { mint : { token_id : "3", owner : accounts[1].seiAddress, token_uri: "token uri 3"}});

const pointerAddr = await deployErc721PointerForCw721(hre.ethers.provider, cw721Address)
const contract = new hre.ethers.Contract(pointerAddr, erc721Abi, hre.ethers.provider);
const contract = new hre.ethers.Contract(pointerAddr, ABI.ERC721, hre.ethers.provider);
pointerAcc0 = contract.connect(accounts[0].signer)
pointerAcc1 = contract.connect(accounts[1].signer)
})

describe("validation", function(){
it("should not allow a pointer to the pointer", async function(){
try {
await registerPointerForCw721(await pointerAcc0.getAddress())
expect.fail(`Expected to be prevented from creating a pointer`);
} catch(e){
expect(e.message).to.include("contract deployment failed");
}
})
})

describe("read", function(){
it("get name", async function () {
const name = await pointerAcc0.name();
Expand Down
76 changes: 72 additions & 4 deletions contracts/test/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,45 @@ const { exec } = require("child_process"); // Importing exec from child_process

const adminKeyName = "admin"

const ABI = {
ERC20: [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function decimals() view returns (uint8)",
"function totalSupply() view returns (uint256)",
"function balanceOf(address owner) view returns (uint256 balance)",
"function transfer(address to, uint amount) returns (bool)",
"function allowance(address owner, address spender) view returns (uint256)",
"function approve(address spender, uint256 value) returns (bool)",
"function transferFrom(address from, address to, uint value) returns (bool)"
],
ERC721: [
"event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId)",
"event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)",
"event ApprovalForAll(address indexed owner, address indexed operator, bool approved)",
"function name() view returns (string)",
"function symbol() view returns (string)",
"function totalSupply() view returns (uint256)",
"function tokenURI(uint256 tokenId) view returns (string)",
"function balanceOf(address owner) view returns (uint256 balance)",
"function ownerOf(uint256 tokenId) view returns (address owner)",
"function getApproved(uint256 tokenId) view returns (address operator)",
"function isApprovedForAll(address owner, address operator) view returns (bool)",
"function approve(address to, uint256 tokenId) returns (bool)",
"function setApprovalForAll(address operator, bool _approved) returns (bool)",
"function transferFrom(address from, address to, uint256 tokenId) returns (bool)",
"function safeTransferFrom(address from, address to, uint256 tokenId) returns (bool)",
"function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) returns (bool)"
],
}

const WASM = {
CW721: "../contracts/wasm/cw721_base.wasm",
CW20: "../contracts/wasm/cw20_base.wasm",
POINTER_CW20: "../example/cosmwasm/cw20/artifacts/cwerc20.wasm",
POINTER_CW721: "../example/cosmwasm/cw721/artifacts/cwerc721.wasm",
}

function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Expand Down Expand Up @@ -116,15 +155,17 @@ async function getPointerForCw721(cw721Address) {
return JSON.parse(output);
}

async function deployErc20PointerForCw20(provider, cw20Address) {
async function deployErc20PointerForCw20(provider, cw20Address, attempts=10) {
const command = `seid tx evm call-precompile pointer addCW20Pointer ${cw20Address} --from=admin -b block`
const output = await execute(command);
const txHash = output.replace(/.*0x/, "0x").trim()
let attempt = 0;
while(attempt < 10) {
while(attempt < attempts) {
const receipt = await provider.getTransactionReceipt(txHash);
if(receipt) {
if(receipt && receipt.status === 1) {
return (await getPointerForCw20(cw20Address)).pointer
} else if(receipt){
throw new Error("contract deployment failed")
}
await sleep(500)
attempt++
Expand All @@ -139,8 +180,10 @@ async function deployErc721PointerForCw721(provider, cw721Address) {
let attempt = 0;
while(attempt < 10) {
const receipt = await provider.getTransactionReceipt(txHash);
if(receipt) {
if(receipt && receipt.status === 1) {
return (await getPointerForCw721(cw721Address)).pointer
} else if(receipt){
throw new Error("contract deployment failed")
}
await sleep(500)
attempt++
Expand All @@ -161,6 +204,27 @@ async function instantiateWasm(codeId, adminAddr, label, args = {}) {
return getEventAttribute(response, "instantiate", "_contract_address");
}

async function registerPointerForCw20(erc20Address, fees="20000usei", from=adminKeyName) {
const command = `seid tx evm register-pointer ERC20 ${erc20Address} --from ${from} --fees ${fees} --broadcast-mode block -y -o json`
const output = await execute(command);
const response = JSON.parse(output)
if(response.code !== 0) {
throw new Error("contract deployment failed")
}
return getEventAttribute(response, "pointer_registered", "pointer_address")
}

async function registerPointerForCw721(erc721Address, fees="20000usei", from=adminKeyName) {
const command = `seid tx evm register-pointer ERC721 ${erc721Address} --from ${from} --fees ${fees} --broadcast-mode block -y -o json`
const output = await execute(command);
const response = JSON.parse(output)
if(response.code !== 0) {
throw new Error("contract deployment failed")
}
return getEventAttribute(response, "pointer_registered", "pointer_address")

}


async function getSeiAddress(evmAddress) {
const command = `seid q evm sei-addr ${evmAddress} -o json`
Expand Down Expand Up @@ -276,11 +340,15 @@ module.exports = {
deployEvmContract,
deployErc20PointerForCw20,
deployErc721PointerForCw721,
registerPointerForCw20,
registerPointerForCw721,
importKey,
getNativeAccount,
associateKey,
delay,
bankSend,
evmSend,
waitForReceipt,
WASM,
ABI,
};
Loading

0 comments on commit 8b1f76d

Please sign in to comment.