Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ Tests are configured via environment variables:

- `START_ETH_RPC=1` - Automatically start revive eth-rpc server
- `ETH_RPC_PATH` - Path to the eth-rpc binary (default: to ~/polkadot-sdk/target/debug/eth-rpc)
- `USE_REVIVE=evm|pvm` - Whether to run tests against revive with evm or pvm bytecode, default to `evm` if not specified
- `USE_REVIVE=evm|pvm` - Whether to run tests against revive with evm or pvm bytecode, default to `evm` if not specified

- `START_REVIVE_DEV_NODE=1` - Start Revive dev node
- `REVIVE_DEV_NODE_PATH` - Path to the Revive dev node binary (default: to ~/polkadot-sdk/target/debug/revive-dev-node)

22 changes: 22 additions & 0 deletions contracts/ReturnDataTester.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./contracts/Errors.sol";

// Parent contract that creates a child and captures return data
contract ReturnDataTester {
uint public returndatasize;

function createChildContract() external {
new Errors();
uint size;
assembly {
size := returndatasize()
}
returndatasize = size;
}

// Read-only function to return the captured return data size
function getCapturedReturnDataSize() external view returns (uint) {
return returndatasize;
}
}
96 changes: 72 additions & 24 deletions src/others.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { expect } from '@std/expect'
import { decodeEventLog, encodeFunctionData, parseEther } from 'viem'
import { ErrorsAbi } from '../codegen/abi/Errors.ts'
import { EventExampleAbi } from '../codegen/abi/EventExample.ts'
import { ReturnDataTesterAbi } from '../codegen/abi/ReturnDataTester.ts'

// Initialize test environment
const env = await getEnv()
Expand All @@ -31,6 +32,15 @@ const getEventExampleAddr = memoizedDeploy(
}),
)

const getReturnDataTesterAddr = memoizedDeploy(
env,
() =>
env.serverWallet.deployContract({
abi: ReturnDataTesterAbi,
bytecode: getByteCode('ReturnDataTester', env.evm),
}),
)

Deno.test('eth_call with insufficient funds', opts, async () => {
try {
await env.emptyWallet.simulateContract({
Expand All @@ -44,18 +54,14 @@ Deno.test('eth_call with insufficient funds', opts, async () => {
} catch (_) {
const lastJsonRpcError = jsonRpcErrors.pop()
expect(lastJsonRpcError?.code).toBe(-32000)
expect(lastJsonRpcError?.message).toContain(
'insufficient funds',
)
expect(lastJsonRpcError?.message).toContain('insufficient funds')
expect(lastJsonRpcError?.data).toBeUndefined()
}
})

Deno.test('eth_call transfer with insufficient funds', opts, async () => {
const value = parseEther('10')
const balance = await env.emptyWallet.getBalance(
env.emptyWallet.account,
)
const balance = await env.emptyWallet.getBalance(env.emptyWallet.account)
if (balance >= value) {
throw new Error('Balance should be less than 10')
}
Expand All @@ -68,9 +74,7 @@ Deno.test('eth_call transfer with insufficient funds', opts, async () => {
} catch (_) {
const lastJsonRpcError = jsonRpcErrors.pop()
expect(lastJsonRpcError?.code).toBe(-32000)
expect(lastJsonRpcError?.message).toContain(
'insufficient funds',
)
expect(lastJsonRpcError?.message).toContain('insufficient funds')
expect(lastJsonRpcError?.data).toBeUndefined()
}
})
Expand All @@ -88,9 +92,7 @@ Deno.test('eth_estimate with insufficient funds', opts, async () => {
} catch (_err) {
const lastJsonRpcError = jsonRpcErrors.pop()
expect(lastJsonRpcError?.code).toBe(-32000)
expect(lastJsonRpcError?.message).toContain(
'insufficient funds',
)
expect(lastJsonRpcError?.message).toContain('insufficient funds')
expect(lastJsonRpcError?.data).toBeUndefined()
}
})
Expand All @@ -111,9 +113,7 @@ Deno.test(
} catch (_err) {
const lastJsonRpcError = jsonRpcErrors.pop()
expect(lastJsonRpcError?.code).toBe(-32000)
expect(lastJsonRpcError?.message).toContain(
'insufficient funds',
)
expect(lastJsonRpcError?.message).toContain('insufficient funds')
expect(lastJsonRpcError?.data).toBeUndefined()
}
},
Expand Down Expand Up @@ -168,18 +168,14 @@ Deno.test(
} catch (_err) {
const lastJsonRpcError = jsonRpcErrors.pop()
expect(lastJsonRpcError?.code).toBe(-32000)
expect(lastJsonRpcError?.message).toContain(
'insufficient funds',
)
expect(lastJsonRpcError?.message).toContain('insufficient funds')
expect(lastJsonRpcError?.data).toBeUndefined()
}
},
)

Deno.test('eth_estimate with no gas specified', opts, async () => {
const balance = await env.serverWallet.getBalance(
env.emptyWallet.account,
)
const balance = await env.serverWallet.getBalance(env.emptyWallet.account)
expect(balance).toBe(0n)

const data = encodeFunctionData({
Expand Down Expand Up @@ -209,9 +205,7 @@ Deno.test('logs', opts, async () => {
})

const hash = await env.serverWallet.writeContract(request)
const receipt = await env.serverWallet.waitForTransactionReceipt(
hash,
)
const receipt = await env.serverWallet.waitForTransactionReceipt(hash)
const logs = await env.serverWallet.getLogs({
address,
blockHash: receipt.blockHash,
Expand Down Expand Up @@ -239,3 +233,57 @@ Deno.test('logs', opts, async () => {
},
})
})

Deno.test('returndata_works', opts, async () => {
// 1. deploy ReturnDataTester contract and get its address
const address = await getReturnDataTesterAddr()

// 2. Make child contract code available
await getErrorTesterAddr()

// 3. call createChildContract to create a child contract
const { request } = await env.serverWallet.simulateContract({
address,
abi: ReturnDataTesterAbi,
functionName: 'createChildContract',
})
const hash = await env.serverWallet.writeContract(request)
const receipt = await env.serverWallet.waitForTransactionReceipt(hash)
expect(receipt.status).toEqual('success')

// 4. call getCapturedReturnDataSize to get the recorded return data size
const dataSize = await env.emptyWallet.readContract({
address: address,
abi: ReturnDataTesterAbi,
functionName: 'getCapturedReturnDataSize',
args: [],
})

expect(dataSize).toBe(0n)
})

Deno.test('eth_call_deployment_returns_bytecode', opts, async () => {
const result = await env.serverWallet.call({
data: getByteCode('Errors', env.evm),
})

expect(typeof result).toBe('object')
if (env.evm) {
expect(result).not.toBeNull()
expect('data' in result).toBe(true)
expect(typeof result.data).toBe('string')
const data = result['data']
if (typeof data !== 'string') {
throw new Error(
`expected result.data to be string, got ${typeof data}`,
)
}

// hex string; '0xDDDD...'
expect(data.startsWith('0x')).toBe(true)
expect(data.length).toBeGreaterThan(2)
} else {
// PVM does not return runtime bytecode for contract deployment calls
expect(result.data).toBeUndefined()
}
})
2 changes: 1 addition & 1 deletion src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ export function waitForHealth(url: string) {
resolve()
} catch (_err) {
const elapsed = Date.now() - start
if (elapsed > 30_000) {
if (elapsed > 60_000) {
clearInterval(interval)
reject(new Error('hit timeout'))
}
Expand Down