Arbitrage Functions
Overview
The ArbBot.sol contract is a core component of the A360 AMM’s arbitrage infrastructure. It allows whitelisted market makers (or other specialized addresses) to execute multi-route arbitrage through the A360 Protocol.
Because A360 AMM focuses on Real-World Assets (RWAs) such as publicly and privately traded stocks, the system requires permissioned arbitrage to remain compliant. This permissioning is achieved through the whitelist
mapping, which restricts function calls to approved addresses only. Whitelisted addresses can execute arbitrage feelessly (or under special fee arrangements) to keep on-chain and off-chain prices aligned.
Key Concepts
Whitelist Permissioning
Only addresses in the
whitelist
can call the arbitrage functions.The contract owner can add or remove addresses via
setWhitelist(address user, bool isWhitelist)
.
Profit Distribution
After each arbitrage, profits are separated from principal via the internal function
_transferProfit(amountIn, tokenOut)
.Principal (
amountIn
) is returned to the caller (the whitelisted address), and the profit remainder is transferred to thetreasury
(which can be another contract such as a “Profit Distributor” or the main treasury wallet).
Vault Strategy (Arbitraging with the A360/Balancer V2–Style Vault)
The function
arbFromTokensWithVault
uses the Balancer-stylebatchSwap
mechanism to swap tokens in the A360 Vault.By leveraging
IVault.BatchSwapStep[]
and a single transaction, the ArbBot can perform multiple hops within the Vault to source the best rates.
Function Reference: arbFromTokensWithVault
arbFromTokensWithVault
solidityCopyfunction arbFromTokensWithVault(bytes calldata data)
external
nonReentrant
whenNotPaused
onlyWhitelist
{
(
address vault,
IVault.BatchSwapStep[] memory swaps,
address[] memory assets,
uint256 amountIn,
uint256 amountOut,
uint256 deadline,
uint256 status
) = abi.decode(data, (address, IVault.BatchSwapStep[], address[], uint256, uint256, uint256, uint256));
// 1. Transfer & approve
uint256 len = swaps.length;
for (uint256 i = 0; i < len; i++) {
uint256 stepAmount = swaps[i].amount;
if (stepAmount > 0) {
IERC20Upgradeable tokenIn = IERC20Upgradeable(assets[swaps[i].assetInIndex]);
tokenIn.safeTransferFrom(msg.sender, address(this), stepAmount);
tokenIn.safeIncreaseAllowance(vault, stepAmount);
}
}
// 2. Execute batchSwap
IVault.FundManagement memory funds = IVault.FundManagement({
sender: address(this),
fromInternalBalance: false,
recipient: payable(address(this)),
toInternalBalance: false
});
IVault(vault).batchSwap(
IVault.SwapKind.GIVEN_IN,
swaps,
assets,
funds,
getLimitsForVault(assets.length),
deadline
);
// 3. Transfer profit
IERC20Upgradeable tokenOut = IERC20Upgradeable(assets[swaps[len - 1].assetOutIndex]);
_transferProfit(amountIn, tokenOut);
}
Purpose
Executes a batch swap in the A360 Vault, moving from an input token to a final output token. Once the swaps are complete, the function checks for profit and distributes it accordingly.
Parameters (Decoded from bytes data
)
bytes data
)vault
: The address of the A360 Vault contract implementingIVault
.swaps
: An array ofIVault.BatchSwapStep
structs, each describing one step of the multi-hop swap.poolId
: ID of the pool to swap with.assetInIndex
/assetOutIndex
: Index of the token in theassets
array to swap in/out.amount
: The token amount used or received in that swap step (depending onSwapKind
).userData
: Extra data for the pool (rarely needed for standard Weighted Pools).
assets
: An array of token addresses used in this batch swap.amountIn
: The principal amount that the ArbBot expects to use in the arbitrage.amountOut
: (Not directly used in the function logic but can be included in thedata
for off-chain or mid-swap reference.)deadline
: The UNIX timestamp by which the swap must complete.status
: A generic field that can store custom data or flags for the aggregator (not used in the core logic).
Process Flow
Transfer & Approve For each swap step, if a positive
amount
is specified, ArbBot pulls the required tokens from the caller (msg.sender
) and approves them for the Vault to execute the swap.Execute
batchSwap
UsingIVault.SwapKind.GIVEN_IN
, ArbBot callsbatchSwap
.The
FundManagement
struct directs swapped tokens to return back toaddress(this)
.getLimitsForVault
sets high limits to avoid reverts due to the Vault’s built-in limit checks.
Profit Transfer
After the swap completes, the final output token balance in ArbBot is checked via
_transferProfit(amountIn, tokenOut)
.If
amountOut <= amountIn
, the transaction reverts withErrors.NO_PROFIT
.Otherwise,
amountIn
is returned to the arbitrageur, and the difference (profit) is sent totreasury
.
Access Control
onlyWhitelist
: Restricts this function to addresses inwhitelist
.nonReentrant
andwhenNotPaused
: Standard modifiers to protect against re-entrancy attacks and paused state.
Returns
This function does not return a value directly. However, the arbitrageur receives:
Their original principal (
amountIn
) back, plusThe entire swapped output is momentarily held by ArbBot, from which the profit is separated and sent to
treasury
.
Events
No explicit events are emitted by arbFromTokensWithVault
in the current code. The underlying Vault emits Swap
events for each step of the batch swap.
Underlying Mechanics: Vault Strategy
The ArbBot calls the A360 (Balancer-like) Vault via batchSwap
. Internally, this logic can be delegated to a separate strategy contract (e.g., VaultAMMStrategy.sol). The key takeaway is that Vault-based swaps allow:
Multi-hop routes within a single transaction
Potentially better prices by combining liquidity from multiple pools
Lower gas overhead compared to multiple separate swaps on external DEXs
Last updated