ethers.js/lib.esm/_tests/test-contract.js
2023-05-18 17:26:59 -04:00

360 lines
16 KiB
JavaScript

import assert from "assert";
import { getProvider, setupProviders } from "./create-provider.js";
import { Contract, EventLog, isError, Typed, Wallet } from "../index.js";
setupProviders();
describe("Test Contract", function () {
const addr = "0x99417252Aad7B065940eBdF50d665Fb8879c5958";
const abi = [
"error CustomError1(uint256 code, string message)",
"event EventUint256(uint256 indexed value)",
"event EventAddress(address indexed value)",
"event EventString(string value)",
"event EventBytes(bytes value)",
"function testCustomError1(bool pass, uint code, string calldata message) pure returns (uint256)",
"function testErrorString(bool pass, string calldata message) pure returns (uint256)",
"function testPanic(uint256 code) returns (uint256)",
"function testEvent(uint256 valueUint256, address valueAddress, string valueString, bytes valueBytes) public",
"function testCallAdd(uint256 a, uint256 b) pure returns (uint256 result)",
];
it("tests contract calls", async function () {
this.timeout(10000);
const provider = getProvider("InfuraProvider", "goerli");
const contract = new Contract(addr, abi, provider);
assert.equal(await contract.testCallAdd(4, 5), BigInt(9), "testCallAdd(4, 5)");
assert.equal(await contract.testCallAdd(6, 0), BigInt(6), "testCallAdd(6, 0)");
});
it("tests events", async function () {
this.timeout(60000);
const provider = getProvider("InfuraProvider", "goerli");
assert.ok(provider);
const contract = new Contract(addr, abi, provider);
const signer = new Wallet((process.env.FAUCET_PRIVATEKEY), provider);
const contractSigner = contract.connect(signer);
const vUint256 = 42;
const vAddrName = "ethers.eth";
const vAddr = "0x228568EA92aC5Bc281c1E30b1893735c60a139F1";
const vString = "Hello";
const vBytes = "0x12345678";
let hash = null;
// Test running a listener for a specific event
const specificEvent = new Promise((resolve, reject) => {
contract.on("EventUint256", async (value, event) => {
// Triggered by someone else
if (hash == null || hash !== event.log.transactionHash) {
return;
}
try {
assert.equal(event.filter, "EventUint256", "event.filter");
assert.equal(event.fragment.name, "EventUint256", "event.fragment.name");
assert.equal(event.log.address, addr, "event.log.address");
assert.equal(event.args.length, 1, "event.args.length");
assert.equal(event.args[0], BigInt(42), "event.args[0]");
const count = await contract.listenerCount("EventUint256");
await event.removeListener();
assert.equal(await contract.listenerCount("EventUint256"), count - 1, "decrement event count");
resolve(null);
}
catch (e) {
event.removeListener();
reject(e);
}
});
});
// Test running a listener on all (i.e. "*") events
const allEvents = new Promise((resolve, reject) => {
const waitingFor = {
EventUint256: vUint256,
EventAddress: vAddr,
EventString: vString,
EventBytes: vBytes
};
contract.on("*", (event) => {
// Triggered by someone else
if (hash == null || hash !== event.log.transactionHash) {
return;
}
try {
const name = event.eventName;
assert.equal(event.args[0], waitingFor[name], `${name}`);
delete waitingFor[name];
if (Object.keys(waitingFor).length === 0) {
event.removeListener();
resolve(null);
}
}
catch (error) {
reject(error);
}
});
});
// Send a transaction to trigger some events
const tx = await contractSigner.testEvent(vUint256, vAddr, vString, vBytes);
hash = tx.hash;
const checkEvent = (filter, event) => {
const values = {
EventUint256: vUint256,
EventString: vString,
EventAddress: vAddr,
EventBytes: vBytes
};
assert.ok(event instanceof EventLog, `queryFilter(${filter}):isEventLog`);
const name = event.eventName;
assert.equal(event.address, addr, `queryFilter(${filter}):address`);
assert.equal(event.args[0], values[name], `queryFilter(${filter}):args[0]`);
};
const checkEventFilter = async (filter) => {
const events = (await contract.queryFilter(filter, -10)).filter((e) => (e.transactionHash === hash));
assert.equal(events.length, 1, `queryFilter(${filter}).length`);
checkEvent(filter, events[0]);
return events[0];
};
const receipt = await tx.wait();
// Check the logs in the receipt
for (const log of receipt.logs) {
checkEvent("receipt", log);
}
// Various options for queryFilter
await checkEventFilter("EventUint256");
await checkEventFilter(["EventUint256"]);
await checkEventFilter([["EventUint256"]]);
await checkEventFilter("EventUint256(uint)");
await checkEventFilter(["EventUint256(uint)"]);
await checkEventFilter([["EventUint256(uint)"]]);
await checkEventFilter([["EventUint256", "EventUint256(uint)"]]);
await checkEventFilter("0x85c55bbb820e6d71c71f4894e57751de334b38c421f9c170b0e66d32eafea337");
// Query by Event
await checkEventFilter(contract.filters.EventUint256);
// Query by Deferred Topic Filter; address
await checkEventFilter(contract.filters.EventUint256(vUint256));
// Query by Deferred Topic Filter; address
await checkEventFilter(contract.filters.EventAddress(vAddr));
// Query by Deferred Topic Filter; ENS name => address
await checkEventFilter(contract.filters.EventAddress(vAddrName));
// Multiple Methods
{
const filter = [["EventUint256", "EventString"]];
const events = (await contract.queryFilter(filter, -10)).filter((e) => (e.transactionHash === hash));
assert.equal(events.length, 2, `queryFilter(${filter}).length`);
for (const event of events) {
checkEvent(filter, event);
}
}
await specificEvent;
await allEvents;
});
it("tests the _in_ operator for functions", function () {
const contract = new Contract(addr, abi);
assert.equal("testCallAdd" in contract, true, "has(testCallAdd)");
assert.equal("nonExist" in contract, false, "has(nonExist)");
{
const sig = "function testCallAdd(uint256 a, uint256 b) pure returns (uint256 result)";
assert.equal(sig in contract, true, `has(${sig})`);
assert.equal("function nonExist()" in contract, false, "has(function nonExist())");
}
assert.equal("0xf24684e5" in contract, true, "has(0xf24684e5)");
assert.equal("0xbad01234" in contract, false, "has(0xbad01234)");
});
it("tests the _in_ operator for events", function () {
const contract = new Contract(addr, abi);
assert.equal("EventUint256" in contract.filters, true, "has(EventUint256)");
assert.equal("NonExist" in contract.filters, false, "has(NonExist)");
{
const sig = "event EventUint256(uint256 indexed value)";
assert.equal(sig in contract.filters, true, `has(${sig})`);
assert.equal("event NonExist()" in contract.filters, false, "has(event NonExist())");
}
{
const hash = "0x85c55bbb820e6d71c71f4894e57751de334b38c421f9c170b0e66d32eafea337";
const badHash = "0xbad01234567890ffbad01234567890ffbad01234567890ffbad01234567890ff";
assert.equal(hash in contract.filters, true, `has(${hash})`);
assert.equal(badHash in contract.filters, false, `has(${badHash})`);
}
});
});
describe("Test Typed Contract Interaction", function () {
const tests = [
{
types: ["uint8", "uint16", "uint24", "uint32", "uint40", "uint48", "uint56", "uint64", "uint72", "uint80", "uint88", "uint96", "uint104", "uint112", "uint120", "uint128", "uint136", "uint144", "uint152", "uint160", "uint168", "uint176", "uint184", "uint192", "uint200", "uint208", "uint216", "uint224", "uint232", "uint240", "uint248", "uint256", "int8", "int16", "int24", "int32", "int40", "int48", "int56", "int64", "int72", "int80", "int88", "int96", "int104", "int112", "int120", "int128", "int136", "int144", "int152", "int160", "int168", "int176", "int184", "int192", "int200", "int208", "int216", "int224", "int232", "int240", "int248", "int256"],
valueFunc: (type) => { return 42; }
},
{
types: [
"bytes1", "bytes2", "bytes3", "bytes4", "bytes5", "bytes6", "bytes7", "bytes8", "bytes9", "bytes10", "bytes11", "bytes12", "bytes13", "bytes14", "bytes15", "bytes16", "bytes17", "bytes18", "bytes19", "bytes20", "bytes21", "bytes22", "bytes23", "bytes24", "bytes25", "bytes26", "bytes27", "bytes28", "bytes29", "bytes30", "bytes31", "bytes32",
"bytes"
],
valueFunc: (type) => {
const length = type.substring(5);
if (length) {
const value = new Uint8Array(parseInt(length));
value.fill(42);
return value;
}
return "0x123456";
}
}, {
types: ["bool"],
valueFunc: (type) => { return true; }
}, {
types: ["address"],
valueFunc: (type) => { return "0x643aA0A61eADCC9Cc202D1915D942d35D005400C"; }
}, {
types: ["string"],
valueFunc: (type) => { return "someString"; }
}
];
const abi = [];
for (let i = 1; i <= 32; i++) {
abi.push(`function testTyped(uint${i * 8}) public pure returns (string memory)`);
abi.push(`function testTyped(int${i * 8}) public pure returns (string memory)`);
abi.push(`function testTyped(bytes${i}) public pure returns (string memory)`);
}
abi.push(`function testTyped(address) public pure returns (string memory)`);
abi.push(`function testTyped(bool) public pure returns (string memory)`);
abi.push(`function testTyped(bytes memory) public pure returns (string memory)`);
abi.push(`function testTyped(string memory) public pure returns (string memory)`);
const addr = "0x838f41545DA5e18AA0e1ab391085d22E172B7B02";
const provider = getProvider("InfuraProvider", "goerli");
const contract = new Contract(addr, abi, provider);
for (const { types, valueFunc } of tests) {
for (const type of types) {
const value = valueFunc(type);
it(`tests typed value: Typed.from(${type})`, async function () {
this.timeout(10000);
const v = Typed.from(type, value);
const result = await contract.testTyped(v);
assert.equal(result, type);
});
it(`tests typed value: Typed.${type}()`, async function () {
this.timeout(10000);
const v = Typed[type](value);
const result = await contract.testTyped(v);
assert.equal(result, type);
});
}
}
});
describe("Test Contract Fallback", function () {
const tests = [
{
name: "none",
address: "0x0ccdace3d8353fed9b87a2d63c40452923ccdae5",
abi: [],
sendNone: { error: "no fallback" },
sendData: { error: "no fallback" },
sendValue: { error: "no fallback" },
sendDataAndValue: { error: "no fallback" },
},
{
name: "non-payable fallback",
address: "0x3f10193f79a639b11ec9d2ab42a25a4a905a8870",
abi: [
"fallback()"
],
sendNone: { data: "0x" },
sendData: { data: "0x1234" },
sendValue: { error: "overrides.value" },
sendDataAndValue: { error: "overrides.value" },
},
{
name: "payable fallback",
address: "0xe2de6b97c5eb9fee8a47ca6c0fa642331e0b6330",
abi: [
"fallback() payable"
],
sendNone: { data: "0x" },
sendData: { data: "0x1234" },
sendValue: { data: "0x" },
sendDataAndValue: { data: "0x1234" },
},
{
name: "receive-only",
address: "0xf8f2afbbe37f6a4520e4db7f99495655aa31229b",
abi: [
"receive()"
],
sendNone: { data: "0x" },
sendData: { error: "overrides.data" },
sendValue: { data: "0x" },
sendDataAndValue: { error: "overrides.data" },
},
{
name: "receive and payable fallback",
address: "0x7d97ca5d9dea1cd0364f1d493252006a3c4e18a0",
abi: [
"fallback() payable",
"receive()"
],
sendNone: { data: "0x" },
sendData: { data: "0x1234" },
sendValue: { data: "0x" },
sendDataAndValue: { data: "0x1234" },
},
{
name: "receive and non-payable fallback",
address: "0x5b59d934f0d22b15e73b5d6b9ae83486b70df67e",
abi: [
"fallback()",
"receive()"
],
sendNone: { data: "0x" },
sendData: { data: "0x" },
sendValue: { data: "0x" },
sendDataAndValue: { error: "overrides" },
},
];
const provider = getProvider("InfuraProvider", "goerli");
const testGroups = [
{
group: "sendNone",
tx: {}
},
{
group: "sendData",
tx: { data: "0x1234" }
},
{
group: "sendValue",
tx: { value: 123 }
},
{
group: "sendDataAndValue",
tx: { data: "0x1234", value: 123 }
},
];
for (const { group, tx } of testGroups) {
for (const test of tests) {
const { name, address, abi } = test;
const send = test[group];
const contract = new Contract(address, abi, provider);
it(`test contract fallback checks: ${group} - ${name}`, async function () {
const func = async function () {
if (abi.length === 0) {
throw new Error("no fallback");
}
assert.ok(contract.fallback);
return await contract.fallback.populateTransaction(tx);
};
if ("data" in send) {
await func();
//const result = await func();
//@TODO: Test for the correct populated tx
//console.log(result);
assert.ok(true);
}
else {
await assert.rejects(func, function (error) {
if (error.message === send.error) {
return true;
}
if (isError(error, "INVALID_ARGUMENT")) {
return error.argument === send.error;
}
console.log("EE", error);
return true;
});
}
});
}
}
});
//# sourceMappingURL=test-contract.js.map