Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

it compiles #6

Merged
merged 20 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 11 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
94 changes: 94 additions & 0 deletions src/builder/Accounts.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.23;

import {Strings} from "./Strings.sol";

library Accounts {
struct ChainAccounts {
uint256 chainId;
QuarkState[] quarkStates;
AssetPositions[] assetPositionsList;
}

// We map this to the Portfolio data structure that the client will already have.
// This includes fields that builder may not necessarily need, however it makes
// the client encoding that much simpler.
struct QuarkState {
address account;
bool hasCode;
bool isQuark;
string quarkVersion;
uint96 quarkNextNonce;
}

// Similarly, this is designed to intentionally reduce the encoding burden for the client
// by making it equivalent in structure to data already in portfolios.
struct AssetPositions {
address asset;
string symbol;
uint256 decimals;
uint256 usdPrice;
AccountBalance[] accountBalances;
}

struct AccountBalance {
address account;
uint256 balance;
}

function findChainAccounts(uint256 chainId, ChainAccounts[] memory chainAccountsList)
internal
pure
returns (ChainAccounts memory found)
{
for (uint256 i = 0; i < chainAccountsList.length; ++i) {
if (chainAccountsList[i].chainId == chainId) {
return found = chainAccountsList[i];
}
}
}

function findAssetPositions(string memory assetSymbol, AssetPositions[] memory assetPositionsList)
internal
pure
returns (AssetPositions memory found)
{
for (uint256 i = 0; i < assetPositionsList.length; ++i) {
if (Strings.stringEqIgnoreCase(assetSymbol, assetPositionsList[i].symbol)) {
return found = assetPositionsList[i];
}
}
}

function findAssetPositions(string memory assetSymbol, uint256 chainId, ChainAccounts[] memory chainAccountsList)
internal
pure
returns (AssetPositions memory found)
{
ChainAccounts memory chainAccounts = findChainAccounts(chainId, chainAccountsList);
return findAssetPositions(
assetSymbol,
chainAccounts.assetPositionsList
);
}

function findQuarkState(address account, Accounts.QuarkState[] memory quarkStates)
internal
pure
returns (Accounts.QuarkState memory state)
{
for (uint256 i = 0; i < quarkStates.length; ++i) {
if (quarkStates[i].account == account) {
return state = quarkStates[i];
}
}
}

function sumBalances(AssetPositions memory assetPositions) internal pure returns (uint256) {
uint256 totalBalance = 0;
for (uint256 j = 0; j < assetPositions.accountBalances.length; ++j) {
kevincheng96 marked this conversation as resolved.
Show resolved Hide resolved
totalBalance += assetPositions.accountBalances[j].balance;
}
return totalBalance;
}
}
154 changes: 154 additions & 0 deletions src/builder/Actions.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.23;

import {CCTP} from "./BridgeRoutes.sol";
import {Strings} from "./Strings.sol";
import {Accounts} from "./Accounts.sol";
import {CodeJarHelper} from "./CodeJarHelper.sol";

import {TransferActions} from "../DeFiScripts.sol";

import {IQuarkWallet} from "quark-core/src/interfaces/IQuarkWallet.sol";

library Actions {
string constant PAYMENT_METHOD_OFFCHAIN = "OFFCHAIN";
string constant PAYMENT_METHOD_PAYCALL = "PAY_CALL";
string constant PAYMENT_METHOD_QUOTECALL = "QUOTE_CALL";

string constant ACTION_TYPE_BRIDGE = "BRIDGE";
string constant ACTION_TYPE_TRANSFER = "TRANSFER";

// With Action, we try to define fields that are as 1:1 as possible with
// the simulate endpoint request schema.
struct Action {
uint256 chainId;
address quarkAccount;
string actionType;
bytes actionContext;
// One of the PAYMENT_METHOD_* constants.
string paymentMethod;
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess we should put a TODO: for paymentMethod?
It seems I can't find where it gets initialized or specified anywhere 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

This is set in #8

// Address of payment token on chainId.
// Null address if the payment method was OFFCHAIN.
address paymentToken;
uint256 paymentMaxCost;
}

struct TransferActionContext {
uint256 amount;
uint256 price;
address token;
uint256 chainId;
address recipient;
}

struct BridgeActionContext {
uint256 amount;
uint256 price;
address token;
uint256 chainId;
address recipient;
uint256 destinationChainId;
}

struct BridgeUSDC {
Accounts.ChainAccounts[] chainAccountsList;
string assetSymbol;
uint256 amount;
uint256 originChainId;
address sender;
uint256 destinationChainId;
address recipient;
}

function bridgeUSDC(BridgeUSDC memory bridge)
internal
pure
returns (IQuarkWallet.QuarkOperation memory/*, QuarkAction memory*/)
{
require(Strings.stringEqIgnoreCase(bridge.assetSymbol, "USDC"));
kevincheng96 marked this conversation as resolved.
Show resolved Hide resolved

Accounts.ChainAccounts memory originChainAccounts =
Accounts.findChainAccounts(bridge.originChainId, bridge.chainAccountsList);

Accounts.AssetPositions memory originUSDCPositions =
Accounts.findAssetPositions("USDC", originChainAccounts.assetPositionsList);

Accounts.QuarkState memory accountState =
Accounts.findQuarkState(bridge.sender, originChainAccounts.quarkStates);

bytes[] memory scriptSources = new bytes[](1);
scriptSources[0] = CCTP.bridgeScriptSource();

// CCTP bridge
return IQuarkWallet.QuarkOperation({
nonce: accountState.quarkNextNonce,
scriptAddress: CodeJarHelper.getCodeAddress(bridge.originChainId, scriptSources[0]),
scriptCalldata: CCTP.encodeBridgeUSDC(
bridge.originChainId,
bridge.destinationChainId,
bridge.amount,
bridge.recipient,
originUSDCPositions.asset
),
scriptSources: scriptSources,
expiry: 99999999999 // TODO: handle expiry
});
}

struct TransferAsset {
Accounts.ChainAccounts[] chainAccountsList;
string assetSymbol;
uint256 amount;
uint256 chainId;
address sender;
address recipient;
}

function transferAsset(TransferAsset memory transfer)
internal
pure
returns (IQuarkWallet.QuarkOperation memory/*, QuarkAction memory*/)
{
// TODO: create quark action and return as well
bytes[] memory scriptSources = new bytes[](1);
scriptSources[0] = type(TransferActions).creationCode;

Accounts.ChainAccounts memory accounts =
Accounts.findChainAccounts(transfer.chainId, transfer.chainAccountsList);

Accounts.AssetPositions memory assetPositions =
Accounts.findAssetPositions(transfer.assetSymbol, accounts.assetPositionsList);

Accounts.QuarkState memory accountState =
Accounts.findQuarkState(transfer.sender, accounts.quarkStates);

bytes memory scriptCalldata;
if (Strings.stringEqIgnoreCase(transfer.assetSymbol, "ETH")) {
// Native token transfer
scriptCalldata = abi.encodeWithSelector(
TransferActions.transferNativeToken.selector,
transfer.recipient,
transfer.amount
);
} else {
// ERC20 transfer
scriptCalldata = abi.encodeWithSelector(
TransferActions.transferERC20Token.selector,
assetPositions.asset,
transfer.recipient,
transfer.amount
);
}

return IQuarkWallet.QuarkOperation({
nonce: accountState.quarkNextNonce,
scriptAddress: CodeJarHelper.getCodeAddress(
transfer.chainId,
type(TransferActions).creationCode
),
scriptCalldata: scriptCalldata,
scriptSources: scriptSources,
expiry: 99999999999 // TODO: handle expiry
});
}
}
Loading
Loading