写入合约
使用 writeContract() 向已部署的智能合约发送状态更改交易。与 readContract 不同,此方法会提交链上交易,消耗 Gas,并返回一个可轮询确认的交易哈希。
方法签名
client.writeContract(params: ContractWriteParams): Promise<ApiResponse<ContractWriteResult>>
ContractWriteParams
import type { ContractWriteParams } from '@one_deploy/sdk';
interface ContractWriteParams {
contractAddress: string; // 目标合约地址
chainId: number; // 交易所在的链
functionName: string; // Solidity 函数名
args?: unknown[]; // 位置参数
abi?: Record<string, unknown>[]; // ABI 数组(如果合约已被索引则可选)
value?: string; // 原生代币金额(以 wei 为单位,如 ETH、MATIC)
gasLimit?: string; // 可选的 Gas 限制覆盖
}
ContractWriteResult
interface ContractWriteResult {
transactionHash: string; // 已提交的交易哈希
chainId: number;
status: 'pending' | 'submitted';
}
收到交易哈希后,使用 getTransactionStatus() 跟踪确认状态。
示例
ERC-20: approve
授权某个 spender 代你转移代币的额度:
import { useOneEngine } from '@one_deploy/sdk';
function ApproveButton({
token,
spender,
amount,
}: {
token: string;
spender: string;
amount: string;
}) {
const { client } = useOneEngine();
async function handleApprove() {
const res = await client.writeContract({
contractAddress: token,
chainId: 137,
functionName: 'approve',
args: [spender, amount], // amount 为最小单位(wei / raw)
});
if (!res.success) {
console.error('Approve failed:', res.error?.message);
return;
}
console.log('Tx submitted:', res.data.transactionHash);
// 现在轮询确认
await waitForConfirmation(client, res.data.transactionHash, 137);
}
return <button onClick={handleApprove}>Approve</button>;
}
ERC-20: transfer
向另一个地址转移代币:
const res = await client.writeContract({
contractAddress: '0xA0b8...3E7a', // Ethereum 上的 USDC
chainId: 1,
functionName: 'transfer',
args: [
'0xRecipient...Address', // to
'1000000', // amount(1 USDC = 1e6,6 位小数代币)
],
});
if (res.success) {
console.log('Transfer submitted:', res.data.transactionHash);
}
ERC-721: safeTransferFrom
安全地转移 NFT(接收方必须实现 onERC721Received):
const res = await client.writeContract({
contractAddress: '0xBC4C...a1F9',
chainId: 1,
functionName: 'safeTransferFrom',
args: [
'0xFromAddress...', // from
'0xToAddress...', // to
42, // tokenId
],
});
if (res.success) {
console.log('NFT transfer submitted:', res.data.transactionHash);
}
带 ETH 金额的自定义合约
调用 payable 函数并附带原生 ETH:
const customAbi = [
{
inputs: [{ name: 'referrer', type: 'address' }],
name: 'mintWithReferral',
outputs: [],
stateMutability: 'payable',
type: 'function',
},
];
const res = await client.writeContract({
contractAddress: '0x5566...7788',
chainId: 1,
functionName: 'mintWithReferral',
args: ['0xReferrerAddress...'],
abi: customAbi,
value: '50000000000000000', // 0.05 ETH(以 wei 为单位)
});
if (res.success) {
console.log('Mint tx:', res.data.transactionHash);
}
提示
使用内置的 parseEther 工具函数或任何 BigNumber 库将人类可读的数值转换为 wei。value 字段始终期望以链的最小单位为计量的字符串。
交易确认流程
writeContract 返回交易哈希后,轮询 getTransactionStatus 等待链上确认:
async function waitForConfirmation(
client: InstanceType<typeof import('@one_deploy/sdk').OneEngineClient>,
txHash: string,
chainId: number,
maxAttempts = 30,
intervalMs = 2000,
): Promise<void> {
for (let i = 0; i < maxAttempts; i++) {
const status = await client.getTransactionStatus(txHash, chainId);
if (status.data.status === 'confirmed') {
console.log('Transaction confirmed in block', status.data.blockNumber);
return;
}
if (status.data.status === 'failed') {
throw new Error(`Transaction failed: ${status.data.reason}`);
}
// status.data.status === 'pending' -- 继续轮询
await new Promise((r) => setTimeout(r, intervalMs));
}
throw new Error('Transaction confirmation timed out');
}
getTransactionStatus 响应
interface TransactionStatus {
transactionHash: string;
chainId: number;
status: 'pending' | 'confirmed' | 'failed';
blockNumber?: number;
gasUsed?: string;
reason?: string; // 当 status 为 'failed' 时存在
}
完整 React 示例
在单个组件中组合写入和确认:
import { useOneEngine } from '@one_deploy/sdk';
import { useState } from 'react';
function TransferToken() {
const { client } = useOneEngine();
const [status, setStatus] = useState<string>('idle');
async function handleTransfer() {
setStatus('submitting');
const res = await client.writeContract({
contractAddress: '0xA0b8...3E7a',
chainId: 137,
functionName: 'transfer',
args: ['0xRecipient...', '5000000'], // 5 USDC
});
if (!res.success) {
setStatus(`error: ${res.error?.message}`);
return;
}
setStatus('pending');
const txHash = res.data.transactionHash;
// 轮询确认
const interval = setInterval(async () => {
const txRes = await client.getTransactionStatus(txHash, 137);
if (txRes.data.status === 'confirmed') {
clearInterval(interval);
setStatus('confirmed');
} else if (txRes.data.status === 'failed') {
clearInterval(interval);
setStatus(`failed: ${txRes.data.reason}`);
}
}, 2000);
}
return (
<div>
<button onClick={handleTransfer} disabled={status === 'submitting' || status === 'pending'}>
Transfer 5 USDC
</button>
<p>Status: {status}</p>
</div>
);
}
错误处理
const res = await client.writeContract({
contractAddress: '0xToken...',
chainId: 1,
functionName: 'transfer',
args: ['0xTo...', '1000000000000000000'],
});
if (!res.success) {
switch (res.error?.code) {
case 'INSUFFICIENT_FUNDS':
console.error('Not enough balance to cover the transfer + gas.');
break;
case 'EXECUTION_REVERTED':
console.error('Contract reverted:', res.error.message);
break;
case 'GAS_ESTIMATION_FAILED':
console.error('Could not estimate gas. The transaction would likely revert.');
break;
case 'INVALID_ARGS':
console.error('Arguments do not match the function signature.');
break;
default:
console.error('Unexpected error:', res.error?.message);
}
}
下一步
- 读取合约 -- 在不发送交易的情况下查询链上状态。
- 部署合约 -- 从模板或自定义字节码部署新合约。
- 合约事件与 Webhook -- 订阅合约发出的事件。