import { Address } from 'viem'; import { PERMIT2_MAP, TRANSFER_DISPATCHER, TransferRequest, } from '@prex0/prex-structs'; import { PrexUser } from '../types'; import { PrexSigner } from '../core/sign'; import { PrexApiService } from '../api'; import { PrexClient } from '../prex-client'; import { normalizeErrorFn, PrexSDKError } from '../errors'; import { encodeMetadata } from '../utils/metadata'; export interface TransferOptions { nonce?: bigint; } export class TransferAction { constructor( private client: PrexClient, private user: PrexUser, private signer: PrexSigner, private apiService: PrexApiService ) {} async transfer( params: { token: Address; recipient: Address; amount: bigint; metadata?: Record; sender?: Address; }, options?: TransferOptions ) { const dispatcher = TRANSFER_DISPATCHER; const nonce = await this.getLatestNonce(params.sender, options); const sender = params.sender || this.user.address; const deadline = BigInt(Math.floor(Date.now() / 1000) + 1 * 60 * 60); const request = new TransferRequest( { dispatcher, sender: sender, deadline: deadline, nonce: nonce, amount: params.amount, token: params.token, recipient: params.recipient, metadata: params.metadata ? encodeMetadata(params.metadata) : '0x', }, this.apiService.chainId, PERMIT2_MAP[this.apiService.chainId] ); const signature = await this._signTransferRequest(request); try { return await this.apiService.transfer(request.serialize(), signature); } catch (e) { const normalizedError = normalizeErrorFn('Failed to transfer'); const error = normalizedError(e); throw PrexSDKError.fromError(error); } } async _signTransferRequest(request: TransferRequest) { if (!this.signer) { throw new Error('Signer not initialized'); } const { domain, types, message } = request.permitData(); const signature = await this.signer.signTypedData( { domain, types, message, primaryType: 'PermitWitnessTransferFrom', }, request.params.sender ); return signature; } async getLatestNonce(sender?: Address, options?: TransferOptions) { const specifiedNonce = options?.nonce; if (specifiedNonce) { return specifiedNonce; } const dispatcher = TRANSFER_DISPATCHER; await this.client.updatePermit2Nonce(dispatcher, sender); return this.client.getPermit2Nonce(); } }