{
"id": "app.gainforest.wallet.pendingSend",
"defs": {
"main": {
"key": "literal:self",
"type": "record",
"record": {
"type": "object",
"required": [
"token",
"to",
"amountUnits",
"userOp",
"hash",
"lightHash",
"threshold",
"approvals",
"createdBy",
"createdAt"
],
"properties": {
"to": {
"type": "string",
"description": "Recipient address (0x…, 20 bytes). Redundant with callData, for display; the server re-derives it from the operation."
},
"hash": {
"type": "string",
"description": "Full EntryPoint v0.7 userOp hash — signed by the final approver."
},
"token": {
"enum": [
"ETH",
"USDC",
"USDT"
],
"type": "string",
"description": "What is being sent."
},
"userOp": {
"ref": "#userOp",
"type": "ref"
},
"approvals": {
"type": "array",
"items": {
"ref": "#approval",
"type": "ref"
},
"description": "Light-hash approvals collected so far, in signing order (at most threshold − 1; the final approval is never stored)."
},
"createdAt": {
"type": "string",
"format": "datetime"
},
"createdBy": {
"type": "string",
"format": "did",
"description": "DID of the signed-in account that started the transfer."
},
"lightHash": {
"type": "string",
"description": "SmartVault light userOp hash — signed by every earlier approver."
},
"threshold": {
"type": "integer",
"maximum": 255,
"minimum": 2,
"description": "Approval requirement of the wallet when this transfer was started; a mismatch with the current wallet invalidates the transfer."
},
"amountUnits": {
"type": "string",
"description": "Raw token units as a decimal string. Redundant with callData, for display."
}
}
},
"description": "A transfer from the account's donation wallet (app.gainforest.wallet.primary) that still needs more passkey approvals. Multi-approval wallets (threshold > 1) collect approvals asynchronously: the initiator stores the exact unsigned ERC-4337 UserOperation here together with their own approval, remaining passkey holders approve from their own devices, and the final approver signs the full userOp hash which submits the operation on-chain and deletes this record. Every stored approval is a WebAuthn assertion over the operation's LIGHT hash (the SmartVault scheme where all but the last signer sign a gas-bounded subset of the operation). Approvals are verified off-chain before being stored and re-verified before submission, so a hand-crafted record cannot forge or brick a transfer. Fixed rkey `self`: one pending transfer per wallet at a time."
},
"userOp": {
"type": "object",
"required": [
"sender",
"nonce",
"callData",
"callGasLimit",
"verificationGasLimit",
"preVerificationGas",
"maxFeePerGas",
"maxPriorityFeePerGas"
],
"properties": {
"nonce": {
"type": "string",
"description": "EntryPoint nonce at preparation time; consumed nonces invalidate the transfer."
},
"sender": {
"type": "string",
"description": "The wallet's vault address."
},
"factory": {
"type": "string",
"description": "SmartVaultFactory address — present only while the vault is undeployed."
},
"callData": {
"type": "string",
"description": "vault.execute(...) calldata — exactly one ETH/USDC/USDT transfer."
},
"factoryData": {
"type": "string",
"description": "createAccount calldata deploying the vault on first send."
},
"callGasLimit": {
"type": "string"
},
"maxFeePerGas": {
"type": "string"
},
"preVerificationGas": {
"type": "string"
},
"maxPriorityFeePerGas": {
"type": "string"
},
"verificationGasLimit": {
"type": "string"
}
},
"description": "The unsigned ERC-4337 v0.7 UserOperation, exactly as it will be submitted. Gas and fee fields are decimal strings."
},
"approval": {
"type": "object",
"required": [
"credentialId",
"authenticatorData",
"clientDataJSON",
"challengeIndex",
"typeIndex",
"r",
"s",
"addedBy",
"addedAt"
],
"properties": {
"r": {
"type": "string",
"description": "secp256r1 signature r, decimal string."
},
"s": {
"type": "string",
"description": "secp256r1 signature s (low-s), decimal string."
},
"addedAt": {
"type": "string",
"format": "datetime"
},
"addedBy": {
"type": "string",
"format": "did",
"description": "DID of the signed-in account that added this approval."
},
"typeIndex": {
"type": "integer",
"description": "Index of \"type\" in clientDataJSON."
},
"credentialId": {
"type": "string",
"description": "WebAuthn credential id of the enrolled signer that approved."
},
"challengeIndex": {
"type": "integer",
"description": "Index of \"challenge\" in clientDataJSON."
},
"clientDataJSON": {
"type": "string",
"description": "Client data JSON of the assertion."
},
"authenticatorData": {
"type": "string",
"description": "Authenticator data of the assertion, hex."
}
},
"description": "A WebAuthn assertion over the light userOp hash by one enrolled passkey signer."
}
},
"$type": "com.atproto.lexicon.schema",
"lexicon": 1
}