Skip to main content

Subaccount

Smart Contract as Subaccount

Users can have multiple wallets to create sub-accounts, but managing private keys for different accounts can be inconvenient. To address this issue, we use a specially designed contract as a trading account. The contract is owned by the user's wallet address, which eliminates the need for users to change their wallet address. Additionally, this design makes it easy for other protocols to build on JOJO.

Subaccount Factory

The newSubaccount() function is present inside the Subaccount factory. Once this function executes, the factory replicates the Subaccount's implementation contract. However, each clone has its own storage space, meaning the logic remains the same, but the storage state is independent.

/// @notice https://eips.ethereum.org/EIPS/eip-1167[EIP 1167]
/// is a standard protocol for deploying minimal proxy contracts,
/// also known as "clones".
function newSubaccount() external returns (address subaccount) {
subaccount = Clones.clone(template);
Subaccount(subaccount).init(msg.sender);
subaccountRegistry[msg.sender].push(subaccount);
emit NewSubaccount(msg.sender, subaccountRegistry[msg.sender].length - 1, subaccount);
}

Subaccount

Subaccounts can help their owner manage risk and positions. Users can open orders with isolated positions via Subaccount and let others trade for you by setting them as authorized operators.

Execute for subaccount

The Subaccount allows users to call different methods of various contracts through its execute function, which adds extensibility. As only the account that created the Subaccount can trigger the calls, there are no ownership issues to worry about. Users can quickly deposit or withdraw assets to and from the JOJODealer and open positions using the execute method.

function execute(
address to,
bytes calldata data,
uint256 value
)
external
payable
onlyOwner
returns (bytes memory)
{
require(to != address(0), "execute address is empty");
require(msg.value == value, "TRANSFER_PAYMENT_NOT_MATCH");
(bool success, bytes memory returnData) = to.call{ value: value }(data);
if (!success) {
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}
emit ExecuteTransaction(owner, address(this), to, data, value);
return returnData;
}