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: 2 additions & 1 deletion FLOW_YIELD_VAULTS_EVM_BRIDGE_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,8 @@ function getPendingRequestIds() returns (uint256[] memory);
// Get pending requests in unpacked arrays (pagination)
function getPendingRequestsUnpacked(uint256 startIndex, uint256 count) returns (...);

// Get pending requests for a user in unpacked arrays (includes native FLOW balances)
// Get pending requests for a user in unpacked arrays (includes tracked-token escrow+refund balances)
// Returns (..., address[] memory balanceTokens, uint256[] memory pendingBalances, uint256[] memory claimableRefundsArray)
function getPendingRequestsByUserUnpacked(address user) returns (...);

// Get single request by ID
Expand Down
17 changes: 12 additions & 5 deletions FRONTEND_INTEGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,19 @@ const [
messages,
vaultIdentifiers,
strategyIdentifiers,
pendingBalance,
claimableRefund,
balanceTokens,
pendingBalances,
claimableRefunds,
] = await contract.getPendingRequestsByUserUnpacked(userAddress);
// pendingBalance = escrowed funds for active pending requests (native FLOW only)
// claimableRefund = funds available to claim via claimRefund() (native FLOW only)
// Use getUserPendingBalance/getClaimableRefund for a specific token

// `balanceTokens` contains the tracked token set configured on the contract.
// The balance arrays are aligned by index and may include disabled tokens if users still have balances/refunds.
const pendingByToken = Object.fromEntries(
balanceTokens.map((token, i) => [token, pendingBalances[i]])
);
const claimableByToken = Object.fromEntries(
balanceTokens.map((token, i) => [token, claimableRefunds[i]])
);
```

#### Get All Pending Requests (Paginated, Admin)
Expand Down
66 changes: 50 additions & 16 deletions cadence/contracts/FlowYieldVaultsEVM.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,17 @@ access(all) contract FlowYieldVaultsEVM {
access(all) struct PendingRequestsInfo {
access(all) let evmAddress: String
access(all) let pendingCount: Int
access(all) let pendingBalance: UFix64
access(all) let claimableRefund: UFix64
/// @notice Pending balances per token address (maps token address string -> UFix64 balance)
access(all) let pendingBalances: {String: UFix64}
Copy link
Copy Markdown
Member

@zhangchiqing zhangchiqing Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the token address is basically EVM.EVMAddress, can we use it as key instead?

If not, I would suggest to mention in the comments something like the token address string is the hex format of EVM.EVMAddress.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did some checks and {EVM.EVMAddress: UFix64} is not viable in Cadence. EVM.EVMAddress is a struct, and Cadence does not allow struct types as dictionary keys.

/// @notice Claimable refunds per token address (maps token address string -> UFix64 balance)
access(all) let claimableRefunds: {String: UFix64}
access(all) let requests: [EVMRequest]

init(evmAddress: String, pendingCount: Int, pendingBalance: UFix64, claimableRefund: UFix64, requests: [EVMRequest]) {
init(evmAddress: String, pendingCount: Int, pendingBalances: {String: UFix64}, claimableRefunds: {String: UFix64}, requests: [EVMRequest]) {
self.evmAddress = evmAddress
self.pendingCount = pendingCount
self.pendingBalance = pendingBalance
self.claimableRefund = claimableRefund
self.pendingBalances = pendingBalances
self.claimableRefunds = claimableRefunds
self.requests = requests
}
}
Expand Down Expand Up @@ -1701,8 +1703,9 @@ access(all) contract FlowYieldVaultsEVM {
Type<[String]>(), // messages
Type<[String]>(), // vaultIdentifiers
Type<[String]>(), // strategyIdentifiers
Type<UInt256>(), // pendingBalance
Type<UInt256>() // claimableRefund
Type<[EVM.EVMAddress]>(), // balanceTokens
Type<[UInt256]>(), // pendingBalances
Type<[UInt256]>() // claimableRefundsArray
],
data: callResult.data
)
Expand All @@ -1717,12 +1720,38 @@ access(all) contract FlowYieldVaultsEVM {
let messages = decoded[7] as! [String]
let vaultIdentifiers = decoded[8] as! [String]
let strategyIdentifiers = decoded[9] as! [String]
let pendingBalanceRaw = decoded[10] as! UInt256
let claimableRefundRaw = decoded[11] as! UInt256
let balanceTokens = decoded[10] as! [EVM.EVMAddress]
let pendingBalancesRaw = decoded[11] as! [UInt256]
let claimableRefundsRaw = decoded[12] as! [UInt256]

// Convert pending balance from wei to UFix64
let pendingBalance = FlowEVMBridgeUtils.uint256ToUFix64(value: pendingBalanceRaw, decimals: 18)
let claimableRefund = FlowEVMBridgeUtils.uint256ToUFix64(value: claimableRefundRaw, decimals: 18)
assert(
balanceTokens.length == pendingBalancesRaw.length
&& balanceTokens.length == claimableRefundsRaw.length,
message: "Balance array length mismatch in ABI decode"
)

// Build per-token balance dictionaries
var pendingBalances: {String: UFix64} = {}
var claimableRefundsMap: {String: UFix64} = {}
var j = 0
while j < balanceTokens.length {
let tokenAddr = balanceTokens[j].toString()
let rawPending = pendingBalancesRaw[j]
let rawRefund = claimableRefundsRaw[j]
pendingBalances[tokenAddr] = rawPending == 0
? 0.0
: FlowYieldVaultsEVM.ufix64FromUInt256(
rawPending,
tokenAddress: balanceTokens[j]
)
claimableRefundsMap[tokenAddr] = rawRefund == 0
? 0.0
: FlowYieldVaultsEVM.ufix64FromUInt256(
rawRefund,
tokenAddress: balanceTokens[j]
)
Comment on lines +1737 to +1752
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New failure mode: ufix64FromUInt256 panics if a tracked ERC20's decimals() is unreachable

ufix64FromUInt256 on a non-native-FLOW token calls FlowEVMBridgeUtils.convertERC20AmountToCadenceAmount, which issues a cross-VM decimals() call. If that call reverts (e.g. the token contract was self-destructed, or a non-ERC20 address was ever mistakenly registered and given balances), the entire getPendingRequestsForEVMAddress script panics for every user who holds a non-zero balance for that token.

Before this PR the Cadence query decoded a single scalar for native FLOW and made no ERC20 decimals() calls. The new loop iterates over every tracked token and queries decimals for any non-zero amount, so this failure mode is new to this PR.

The zero-shortcut (rawPending == 0 ? 0.0 : …) correctly avoids the call for empty slots, but it does not protect users who have live escrow or claimable refunds in a now-defunct token.

Comment on lines +1741 to +1752
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The zero-guard correctly skips the decimals() EVM call for zero balances, but any non-zero ERC20 balance/refund entry will call FlowEVMBridgeUtils.convertERC20AmountToCadenceAmount, which panics if the ERC20's decimals() reverts (non-standard token, self-destructed contract, etc.).

Before this PR, ufix64FromUInt256 was only called inside the request loop for request.amount — so the query only panicked if there were pending requests for the broken token. Now it also fires on non-zero claimable refund entries, even when the user has no pending requests. A user who was refunded from a later-broken ERC20 would have getPendingRequestsForEVMAddress permanently stuck.

The SchedulerHandler is unaffected (it uses getPendingRequestsFromEVMgetPendingRequestsUnpacked, not this path), so worker processing is safe. However, monitoring scripts and any frontend caller hitting this endpoint would be unable to query that user's data.

Worth adding a defensive try/catch around the conversion — fall back to 0.0 with a logged event, rather than panicking the whole query:

// pseudo-pattern
let converted: UFix64 = rawPending == 0 ? 0.0 : (
    FlowYieldVaultsEVM._safeUfix64FromUInt256(rawPending, tokenAddress: balanceTokens[j]) ?? 0.0
)

Or at minimum add a comment noting this invariant so future token additions are made with it in mind.

j = j + 1
}
Comment on lines +1723 to +1754
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new ABI-decode path has no Cadence-side test coverage.

This block is the most complex change in the PR: it decodes three new return values ([EVM.EVMAddress], [UInt256], [UInt256]), iterates them, and calls ufix64FromUInt256 (which itself dispatches on whether the token is native FLOW or an ERC20). A wrong type in the decoded[N] as! cast causes a runtime panic; a wrong decimal conversion silently produces incorrect balances.

All four Cadence test suites pass trivially because none of them call getPendingRequestsForEVMAddress. The get_pending_requests_for_evm_address.cdc script and the code path below are completely unexercised by the Cadence test suite. At minimum, evm_bridge_lifecycle_test.cdc should assert that the returned PendingRequestsInfo.pendingBalances dictionary contains the expected token keys and converted values after a request is created.

Comment thread
liobrasil marked this conversation as resolved.

// Build request array
var requests: [EVMRequest] = []
Expand All @@ -1748,8 +1777,8 @@ access(all) contract FlowYieldVaultsEVM {
return PendingRequestsInfo(
evmAddress: evmAddressHex,
pendingCount: ids.length,
pendingBalance: pendingBalance,
claimableRefund: claimableRefund,
pendingBalances: pendingBalances,
claimableRefunds: claimableRefundsMap,
requests: requests
)
}
Expand Down Expand Up @@ -1947,7 +1976,10 @@ access(all) contract FlowYieldVaultsEVM {

/// @notice Converts a UInt256 amount from EVM to UFix64 for Cadence
/// @dev For native FLOW: Uses 18 decimals (attoflow to FLOW conversion)
/// For ERC20: Uses FlowEVMBridgeUtils to look up token decimals
/// For ERC20: Uses FlowEVMBridgeUtils to look up token decimals.
/// Cadence UFix64 preserves 8 decimal places, so tokens with more than 8
/// decimals are truncated toward zero when entering Cadence. Any remainder
/// smaller than 0.00000001 token is lost and cannot be recovered later.
/// @param value The amount in wei/smallest unit (UInt256)
/// @param tokenAddress The token address to determine decimal conversion
/// @return The converted amount in UFix64 format
Expand All @@ -1960,7 +1992,9 @@ access(all) contract FlowYieldVaultsEVM {

/// @notice Converts a UFix64 amount from Cadence to UInt256 for EVM
/// @dev For native FLOW: Uses 18 decimals (FLOW to attoflow conversion)
/// For ERC20: Uses FlowEVMBridgeUtils to look up token decimals
/// For ERC20: Uses FlowEVMBridgeUtils to look up token decimals.
/// This reconstructs an EVM amount from the already-quantized UFix64 value,
/// so previously truncated sub-1e-8 dust does not reappear on the return path.
/// @param value The amount in UFix64 format
/// @param tokenAddress The token address to determine decimal conversion
/// @return The converted amount in wei/smallest unit (UInt256)
Expand Down
58 changes: 58 additions & 0 deletions cadence/tests/evm_bridge_lifecycle_test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,64 @@ fun testProcessResultStructure() {
Test.assertEqual("Insufficient COA balance", failureResult.message)
}

access(all)
fun testGetPendingRequestsForEVMAddressDecodesBalances() {
let tokenBAddress = deployERC20DecimalsOnlyMock(admin, decimals: 6)
let tokenCAddress = "0x00000000000000000000000000000000000000cc"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test gap: tokenC never exercises the non-zero ERC20 decimal-lookup path

tokenCAddress is a bare address with no deployed contract. The mock (PendingRequestsByUserQueryMock) leaves pendingBalances[2] and claimableRefundsArray[2] at their default of 0, so the rawPending == 0 ? 0.0 : ufix64FromUInt256(…) shortcut in FlowYieldVaultsEVM.cdc means decimals() is never actually called for that address.

This means the risky path—"tracked token with a non-zero amount but an unresponsive ERC20 contract"—has no test coverage at all. Consider adding a second variant of the mock that sets a non-zero balance for tokenC with a non-deployed address to confirm the expected behavior (panic with a clear message, or a graceful fallback).

let mockAddress = deployPendingRequestsByUserQueryMock(admin, tokenBAddress: tokenBAddress, tokenCAddress: tokenCAddress)
let tokenBKey = EVM.addressFromString(tokenBAddress).toString()
let tokenCKey = EVM.addressFromString(tokenCAddress).toString()
let setAddrResult = updateRequestsAddress(admin, mockAddress)
Test.expect(setAddrResult, Test.beSucceeded())

let result = _executeScript(
"../scripts/get_pending_requests_for_evm_address.cdc",
[userEVMAddr1.toString()]
)
Test.assertEqual(Test.ResultStatus.succeeded, result.status)

let pendingInfo = result.returnValue as! FlowYieldVaultsEVM.PendingRequestsInfo
let nativeFlowKey = nativeFlowAddr.toString()
let pendingNative = pendingInfo.pendingBalances[nativeFlowKey]
?? panic("Missing NATIVE_FLOW pending balance entry")
let refundNative = pendingInfo.claimableRefunds[nativeFlowKey]
?? panic("Missing NATIVE_FLOW refund balance entry")
let pendingTokenB = pendingInfo.pendingBalances[tokenBKey]
?? panic("Missing tokenB pending balance entry")
let refundTokenB = pendingInfo.claimableRefunds[tokenBKey]
?? panic("Missing tokenB refund balance entry")
let pendingTokenC = pendingInfo.pendingBalances[tokenCKey]
?? panic("Missing tokenC pending balance entry")
let refundTokenC = pendingInfo.claimableRefunds[tokenCKey]
?? panic("Missing tokenC refund balance entry")

Test.assertEqual(userEVMAddr1.toString(), pendingInfo.evmAddress)
Test.assertEqual(2, pendingInfo.pendingCount)
Test.assertEqual(3.0, pendingNative)
Test.assertEqual(0.0, refundNative)
Test.assertEqual(1.234567, pendingTokenB)
Test.assertEqual(0.5, refundTokenB)
Test.assertEqual(0.0, pendingTokenC)
Test.assertEqual(0.0, refundTokenC)
Test.assertEqual(2, pendingInfo.requests.length)

Test.assertEqual(11 as UInt256, pendingInfo.requests[0].id)
Test.assertEqual(12 as UInt256, pendingInfo.requests[1].id)
Test.assertEqual(1000000000000000000 as UInt256, pendingInfo.requests[0].amount)
Test.assertEqual(1234567 as UInt256, pendingInfo.requests[1].amount)
Test.assertEqual(nil, pendingInfo.requests[0].yieldVaultId)
Test.assertEqual(42 as UInt64?, pendingInfo.requests[1].yieldVaultId)
Test.assertEqual(tokenBKey, pendingInfo.requests[1].tokenAddress.toString())
Test.assertEqual(
FlowYieldVaultsEVM.RequestType.CREATE_YIELDVAULT.rawValue,
pendingInfo.requests[0].requestType
)
Test.assertEqual(
FlowYieldVaultsEVM.RequestType.DEPOSIT_TO_YIELDVAULT.rawValue,
pendingInfo.requests[1].requestType
)
}

access(all)
fun testVaultAndStrategyIdentifiers() {
// Test that vault and strategy identifiers are preserved correctly
Expand Down
33 changes: 33 additions & 0 deletions cadence/tests/test_helpers.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ access(all) let falseApproveTokenBytecode = "608080604052346100155760bd908161001
access(all) let mockVaultIdentifier = "A.0ae53cb6e3f42a79.FlowToken.Vault"
access(all) let mockStrategyIdentifier = "A.045a1763c93006ca.MockStrategies.TracerStrategy"

/* --- Embedded EVM mock bytecode --- */

// Regenerate with the repo's Foundry defaults from solidity/foundry.toml:
// `cd solidity && forge inspect src/test/PendingRequestsByUserQueryMock.sol:ERC20DecimalsOnlyMock bytecode`
access(all) let erc20DecimalsOnlyMockBytecode = "60a03461006257601f61011338819003918201601f19168301916001600160401b0383118484101761006657808492602094604052833981010312610062575160ff81168103610062576080526040516098908161007b823960805181603a0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60808060405260043610156011575f80fd5b5f90813560e01c63313ce567146025575f80fd5b34605e5781600319360112605e5760209060ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fdfea26469706673582212203c866ad60ad80831e09284cb4b396975c6e952f53d1755f246b0f36dcd9c903664736f6c63430008140033"

// Regenerate with the repo's Foundry defaults from solidity/foundry.toml:
// `cd solidity && forge inspect src/test/PendingRequestsByUserQueryMock.sol:PendingRequestsByUserQueryMock bytecode`
access(all) let pendingRequestsByUserQueryMockBytecode = "60c03461008157601f6108dc38819003918201601f19168301916001600160401b0383118484101761008557808492604094855283398101031261008157610052602061004b83610099565b9201610099565b9060805260a05260405161082e90816100ae82396080518181816103f00152610681015260a0518161042e0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036100815756fe60806040526004361015610011575f80fd5b5f803560e01c636b67542814610025575f80fd5b346101875760209081600319360112610187576004356001600160a01b0381168103610183576100549061038c565b9b9c94969d939792989199909a604051809e6101a080835282016100779161018a565b9086818303910152610088916101bd565b8d810360408f0152610099916101bd565b8c810360608e01526100aa916101f3565b8b810360808d01526100bb9161018a565b908a820360a08c015280808d5193848152019c01925b828110610165575050505093610134610152946101256101619895610116610143966101088f8f9e9c8f60c081840391015261018a565b8d810360e08f01529061022f565b908b82036101008d015261022f565b908982036101208b015261022f565b908782036101408901526101f3565b9085820361016087015261018a565b9083820361018085015261018a565b0390f35b835167ffffffffffffffff168d529b81019b928101926001016100d1565b5080fd5b80fd5b9081518082526020808093019301915f5b8281106101a9575050505090565b83518552938101939281019260010161019b565b9081518082526020808093019301915f5b8281106101dc575050505090565b835160ff16855293810193928101926001016101ce565b9081518082526020808093019301915f5b828110610212575050505090565b83516001600160a01b031685529381019392810192600101610204565b9080825180825260208092019180808360051b8601019501935f9384915b84831061025e575050505050505090565b9091929394959684601f198084840301855289518051908185528a5b8281106102a657505080840183018a9052601f011690910181019781019695949360010192019061024d565b81810185015186820186015289940161027a565b604051906020820182811067ffffffffffffffff8211176102da57604052565b634e487b7160e01b5f52604160045260245ffd5b604051906060820182811067ffffffffffffffff8211176102da57604052565b604051906080820182811067ffffffffffffffff8211176102da57604052565b80511561033b5760200190565b634e487b7160e01b5f52603260045260245ffd5b80516001101561033b5760400190565b6103676102ee565b9060028252815f5b6040811061037b575050565b80606060208093850101520161036f565b9061039561030e565b90600382526060366020840137816103ab61030e565b60038152606036602083013780916103c161030e565b9060038252606036602084013790958691906001600160a01b036103e48361032e565b526103ee8261034f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316905281516002101561033b576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660608401521660101901610769575050506104686102ee565b60028152916040366020850137829461047f6102ee565b6002815290604036602084013781956104966102ee565b60028152604036602083013780966104ac6102ee565b6002815292604036602086013783976104c36102ee565b6002815295604036602089013786986104da6102ee565b600281529760403660208b013788996104f16102ee565b600281526040366020830137809a61050761035f565b9a8b61051161035f565b9b8c9561051c61035f565b9c6105268161032e565b600b90525f978894856105388561032e565b52856105438661032e565b526001600160a01b036105558861032e565b5261055f8b61032e565b670de0b6b3a764000090526105738861032e565b67ffffffffffffffff90526105878961032e565b606490526105936102ba565b86815261059f8261032e565b526105a99061032e565b506105b26102ee565b602281527f412e306165353363623665336634326137392e466c6f77546f6b656e2e5661756020820152611b1d60f21b60408201526105f08261032e565b526105fa9061032e565b508d6106046102ee565b603081527f412e303435613137363363393330303663612e4d6f636b53747261746567696560208201526f732e547261636572537472617465677960801b60408201526106508261032e565b5261065a9061032e565b506106649061034f565b600c90526106719061034f565b6001905261067e9061034f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906106b39061034f565b526212d6876106c2819561034f565b526106cc9061034f565b602a90526106d99061034f565b606590526106e56102ba565b8181526106f18a61034f565b526106fb8961034f565b506107046102ba565b8181526107108961034f565b5261071a8861034f565b506107236102ba565b90815261072f8761034f565b526107398661034f565b506107438461032e565b6729a2241af62c000090526107578461034f565b526107618261034f565b6207a1209052565b925092945092506107786102ba565b925f928385526107866102ba565b958487526107926102ba565b9585875261079e6102ba565b958087526107aa6102ba565b958187526107b66102ba565b958287526107c26102ba565b958387526107ce6102ba565b958487526107da6102ba565b958587526107e66102ba565b9586529c9b9a9998979695949392919056fea2646970667358221220f90ed4a66eaa118955dbeba6ec133f3d37c612ed4d77722dcf4cd4ba51d5b4a464736f6c63430008140033"

/* --- Setup helpers --- */

// Deploys all required contracts for FlowYieldVaultsEVM
Expand Down Expand Up @@ -291,6 +301,29 @@ fun deployEVMContract(_ signer: Test.TestAccount, _ bytecode: String, _ gasLimit
return contractAddress
}

access(all)
fun deployEVMContractWithArgs(_ signer: Test.TestAccount, _ bytecode: String, _ args: [AnyStruct]): String {
let argsBytecode = EVM.encodeABI(args)
let bytecodeWithArgs = String.encodeHex(bytecode.decodeHex().concat(argsBytecode))
return deployEVMContract(signer, bytecodeWithArgs, UInt64(15_000_000))
}

access(all)
fun deployERC20DecimalsOnlyMock(_ signer: Test.TestAccount, decimals: UInt8): String {
return deployEVMContractWithArgs(signer, erc20DecimalsOnlyMockBytecode, [decimals])
}

access(all)
fun deployPendingRequestsByUserQueryMock(
_ signer: Test.TestAccount,
tokenBAddress: String,
tokenCAddress: String
): String {
let tokenB = EVM.addressFromString(tokenBAddress)
let tokenC = EVM.addressFromString(tokenCAddress)
return deployEVMContractWithArgs(signer, pendingRequestsByUserQueryMockBytecode, [tokenB, tokenC])
}

access(all)
fun deployFalseApproveToken(_ signer: Test.TestAccount): String {
// Reuse the generic deploy helper so the regression test exercises the same COA
Expand Down
40 changes: 34 additions & 6 deletions deployments/artifacts/FlowYieldVaultsRequests.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@
"type": "receive",
"stateMutability": "payable"
},
{
"type": "function",
"name": "DEFAULT_MINIMUM_BALANCE",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "NATIVE_FLOW",
Expand Down Expand Up @@ -532,14 +545,19 @@
"internalType": "string[]"
},
{
"name": "pendingBalance",
"type": "uint256",
"internalType": "uint256"
"name": "balanceTokens",
"type": "address[]",
"internalType": "address[]"
},
{
"name": "claimableRefund",
"type": "uint256",
"internalType": "uint256"
"name": "pendingBalances",
"type": "uint256[]",
"internalType": "uint256[]"
},
{
"name": "claimableRefundsArray",
"type": "uint256[]",
"internalType": "uint256[]"
}
],
"stateMutability": "view"
Expand Down Expand Up @@ -1947,6 +1965,11 @@
"name": "CannotAllowlistZeroAddress",
"inputs": []
},
{
"type": "error",
"name": "CannotBlocklistZeroAddress",
"inputs": []
},
{
"type": "error",
"name": "CannotRegisterSentinelYieldVaultId",
Expand Down Expand Up @@ -1998,6 +2021,11 @@
"name": "InvalidCOAAddress",
"inputs": []
},
{
"type": "error",
"name": "InvalidMinimumBalance",
"inputs": []
},
{
"type": "error",
"name": "InvalidRequestState",
Expand Down
Loading
Loading