All checks were successful
deploy / deploy (push) Successful in 24s
Proof: The active NEAR Intents funded market-maker loop needs a first-class operator withdrawal action so funded inventory can be exited through repo-controlled code rather than manual follow-up. Assumptions: The configured signer key is also a full-access key on the named NEAR account, and external-chain exits for active OMFT assets are triggered by intents.near::ft_withdraw with the token contract as receiver_id plus memo=WITHDRAW_TO:<destination>. Still fake: Strategy and executor remain disarmed, no live inventory is credited yet, and no live mainnet trade quote has been submitted.
113 lines
3.1 KiB
JavaScript
113 lines
3.1 KiB
JavaScript
import { Account, JsonRpcProvider, KeyPair, teraToGas } from 'near-api-js';
|
|
|
|
import { postJson } from '../../lib/http.mjs';
|
|
|
|
export function createVerifierClient({
|
|
nearRpcUrl,
|
|
verifierContract,
|
|
accountId = '',
|
|
signerPrivateKey = '',
|
|
}) {
|
|
const provider = new JsonRpcProvider({ url: nearRpcUrl });
|
|
const signer = signerPrivateKey ? KeyPair.fromString(signerPrivateKey) : null;
|
|
const operatorAccount = accountId && signerPrivateKey
|
|
? new Account(accountId, provider, signerPrivateKey)
|
|
: null;
|
|
|
|
return {
|
|
async currentSalt() {
|
|
return callView(provider, verifierContract, 'current_salt', {});
|
|
},
|
|
async mtBatchBalanceOf({ accountId, tokenIds }) {
|
|
const result = await callView(provider, verifierContract, 'mt_batch_balance_of', {
|
|
account_id: accountId,
|
|
token_ids: tokenIds,
|
|
});
|
|
|
|
if (!Array.isArray(result)) {
|
|
throw new Error('Unexpected mt_batch_balance_of response');
|
|
}
|
|
|
|
return Object.fromEntries(tokenIds.map((tokenId, index) => [tokenId, String(result[index] || '0')]));
|
|
},
|
|
async isPublicKeyRegistered({ accountId }) {
|
|
if (!signer) return false;
|
|
const publicKey = signer.getPublicKey().toString();
|
|
const result = await callView(provider, verifierContract, 'has_public_key', {
|
|
account_id: accountId,
|
|
public_key: publicKey,
|
|
}).catch(() => null);
|
|
|
|
if (typeof result === 'boolean') return result;
|
|
return null;
|
|
},
|
|
async ftWithdrawRaw({
|
|
token,
|
|
receiverId,
|
|
amount,
|
|
memo = null,
|
|
msg = null,
|
|
storageDeposit = null,
|
|
}) {
|
|
if (!operatorAccount) throw new Error('operator account is not configured');
|
|
|
|
return operatorAccount.callFunctionRaw({
|
|
contractId: verifierContract,
|
|
methodName: 'ft_withdraw',
|
|
args: compact({
|
|
token,
|
|
receiver_id: receiverId,
|
|
amount: String(amount),
|
|
memo,
|
|
msg,
|
|
storage_deposit: storageDeposit,
|
|
}),
|
|
gas: teraToGas('100'),
|
|
deposit: 1n,
|
|
});
|
|
},
|
|
getSigner() {
|
|
return signer;
|
|
},
|
|
};
|
|
}
|
|
|
|
export function createSolverRelayRpcClient({ rpcUrl }) {
|
|
let id = 1;
|
|
return {
|
|
async getStatus(intentHash) {
|
|
const response = await postJson(rpcUrl, {
|
|
jsonrpc: '2.0',
|
|
id: id++,
|
|
method: 'get_status',
|
|
params: [{ intent_hash: intentHash }],
|
|
});
|
|
|
|
if (response.error) {
|
|
throw new Error(response.error.message || 'Solver Relay get_status failed');
|
|
}
|
|
|
|
return response.result;
|
|
},
|
|
};
|
|
}
|
|
|
|
async function callView(provider, accountId, methodName, args) {
|
|
const response = await provider.query({
|
|
request_type: 'call_function',
|
|
finality: 'final',
|
|
account_id: accountId,
|
|
method_name: methodName,
|
|
args_base64: Buffer.from(JSON.stringify(args)).toString('base64'),
|
|
});
|
|
|
|
const bytes = response.result || [];
|
|
const text = Buffer.from(bytes).toString('utf8');
|
|
return text ? JSON.parse(text) : null;
|
|
}
|
|
|
|
function compact(record) {
|
|
return Object.fromEntries(
|
|
Object.entries(record).filter(([, value]) => value != null),
|
|
);
|
|
}
|