Skip to content

Commit

Permalink
feature (token): added swap functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
H34D committed Feb 7, 2024
1 parent 37f0af8 commit f7e34ca
Show file tree
Hide file tree
Showing 15 changed files with 561 additions and 16 deletions.
4 changes: 3 additions & 1 deletion package.json
Expand Up @@ -41,8 +41,10 @@
"@ethersproject/abstract-signer": "^5.7.0",
"@ethersproject/bignumber": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@layerzerolabs/lz-definitions": "^2.0.25",
"@layerzerolabs/lz-v2-utilities": "^2.0.25",
"@masa-finance/masa-contracts-identity": "^1.12.0",
"@masa-finance/masa-token": "^2.0.0",
"@masa-finance/masa-token": "^2.0.7",
"arweave": "1.11.8",
"axios": "^1.6.7",
"ethers": "~5.7.2",
Expand Down
16 changes: 16 additions & 0 deletions src/collections/networks.ts
@@ -1,3 +1,5 @@
import { EndpointId } from "@layerzerolabs/lz-definitions";

import type { Addresses, Network, NetworkName } from "../interface";
import { addresses } from "../networks";

Expand Down Expand Up @@ -52,6 +54,7 @@ const bsc: Network = {
blockExplorerUrls: ["https://bscscan.com"],
blockExplorerApiUrls: ["https://api.bscscan.com/api"],
addresses: addresses["bsc"] as Addresses,
lzEndpointId: EndpointId.BSC_V2_MAINNET,
};
const bsctest: Network = {
networkName: "bsctest",
Expand All @@ -77,6 +80,7 @@ const bsctest: Network = {
blockExplorerUrls: ["https://testnet.bscscan.com"],
blockExplorerApiUrls: ["https://api-testnet.bscscan.com/api"],
addresses: addresses["bsctest"] as Addresses,
lzEndpointId: EndpointId.BSC_V2_TESTNET,
};

// op bnb
Expand All @@ -103,6 +107,7 @@ const opbnb: Network = {
},
blockExplorerUrls: ["https://opbnbscan.com"],
addresses: addresses["opbnb"] as Addresses,
lzEndpointId: EndpointId.OPBNB_V2_MAINNET,
};
const opbnbtest: Network = {
networkName: "opbnbtest",
Expand All @@ -127,6 +132,7 @@ const opbnbtest: Network = {
},
blockExplorerUrls: ["https://testnet.opbnbscan.com"],
addresses: addresses["opbnbtest"] as Addresses,
lzEndpointId: EndpointId.OPBNB_V2_TESTNET,
};

// celo
Expand Down Expand Up @@ -154,6 +160,7 @@ const celo: Network = {
blockExplorerUrls: ["https://celoscan.io"],
blockExplorerApiUrls: ["https://api.celoscan.io/api"],
addresses: addresses["celo"] as Addresses,
lzEndpointId: EndpointId.CELO_V2_MAINNET,
};
const alfajores: Network = {
networkName: "alfajores",
Expand All @@ -179,6 +186,7 @@ const alfajores: Network = {
blockExplorerUrls: ["https://alfajores.celoscan.io"],
blockExplorerApiUrls: ["https://api-alfajores.celoscan.io/api"],
addresses: addresses["alfajores"] as Addresses,
lzEndpointId: EndpointId.CELO_V2_TESTNET,
};

// polygon
Expand Down Expand Up @@ -208,6 +216,7 @@ const polygon: Network = {
addresses: addresses["polygon"] as Addresses,
// https://github.com/maticnetwork/bor/issues/384
skipEip1559: true,
lzEndpointId: EndpointId.POLYGON_V2_MAINNET,
};
const mumbai: Network = {
networkName: "mumbai",
Expand All @@ -234,6 +243,7 @@ const mumbai: Network = {
blockExplorerApiUrls: ["https://api-mumbai.polygonscan.com/api"],
addresses: addresses["mumbai"] as Addresses,
gasSlippagePercentage: 5000,
lzEndpointId: EndpointId.POLYGON_V2_TESTNET,
};

// ethereum
Expand Down Expand Up @@ -261,6 +271,7 @@ const ethereum: Network = {
blockExplorerUrls: ["https://etherscan.io"],
blockExplorerApiUrls: ["https://api.etherscan.io/api"],
addresses: addresses["ethereum"] as Addresses,
lzEndpointId: EndpointId.ETHEREUM_V2_MAINNET,
};
// @deprecated: use sepolia instead
const goerli: Network = {
Expand Down Expand Up @@ -303,6 +314,7 @@ const sepolia: Network = {
blockExplorerUrls: ["https://sepolia.etherscan.io"],
blockExplorerApiUrls: ["https://api-sepolia.etherscan.io/api"],
addresses: addresses["sepolia"] as Addresses,
lzEndpointId: EndpointId.SEPOLIA_V2_TESTNET,
};

// base mainnet
Expand Down Expand Up @@ -330,6 +342,7 @@ const base: Network = {
blockExplorerUrls: ["https://basescan.org"],
blockExplorerApiUrls: ["https://api.basescan.org/api"],
addresses: addresses["base"] as Addresses,
lzEndpointId: EndpointId.BASE_V2_MAINNET,
};
// @deprecated: use base sepolia instead
const basegoerli: Network = {
Expand Down Expand Up @@ -375,6 +388,7 @@ const basesepolia: Network = {
blockExplorerUrls: ["https://base-sepolia.blockscout.com"],
blockExplorerApiUrls: ["https://api-sepolia.basescan.org/api"],
addresses: addresses["basesepolia"] as Addresses,
lzEndpointId: EndpointId.BASE_V2_TESTNET,
};

// scroll mainnet
Expand All @@ -401,6 +415,7 @@ const scroll: Network = {
],
blockExplorerUrls: ["https://scrollscan.com"],
addresses: addresses["scroll"] as Addresses,
lzEndpointId: EndpointId.SCROLL_V2_MAINNET,
};
const scrollsepolia: Network = {
networkName: "scrollsepolia",
Expand All @@ -425,6 +440,7 @@ const scrollsepolia: Network = {
],
blockExplorerUrls: ["https://sepolia.scrollscan.com"],
addresses: addresses["scrollsepolia"] as Addresses,
lzEndpointId: EndpointId.SCROLL_V2_MAINNET,
};

export const SupportedNetworks: Partial<{
Expand Down
6 changes: 6 additions & 0 deletions src/interface/network.ts
@@ -1,3 +1,5 @@
import { EndpointId } from "@layerzerolabs/lz-definitions";

import type { Addresses } from "./addresses";
import type { NetworkName } from "./network-name";

Expand Down Expand Up @@ -57,4 +59,8 @@ export interface Network {
* skip eip1559 gas price calculation for this network
*/
skipEip1559?: boolean;
/**
* LayerZero Endpoint ID
*/
lzEndpointId?: EndpointId;
}
4 changes: 4 additions & 0 deletions src/masa.ts
Expand Up @@ -13,6 +13,7 @@ import {
MasaSession,
MasaSoulName,
MasaSSSBT,
MasaToken,
version,
} from "./modules";
import { getNetworkNameByChainId, MasaArweave, MasaClient } from "./utils";
Expand All @@ -36,6 +37,8 @@ export class Masa implements MasaInterface {
readonly sbt: MasaSBTBase;
readonly asbt: MasaASBT;
readonly sssbt: MasaSSSBT;
// token
readonly token: MasaToken;
// Dynamic SBTs
readonly ["dynamic-sbt"]: MasaDynamicSBTBase;
readonly ["dynamic-sssbt"]: MasaDynamicSSSBT;
Expand Down Expand Up @@ -98,6 +101,7 @@ export class Masa implements MasaInterface {
this.asbt = new MasaASBT(this);
// SSSBT Handler
this.sssbt = new MasaSSSBT(this);
this.token = new MasaToken(this);
// dynamic sbt
this["dynamic-sbt"] = new MasaDynamicSBTBase(this);
this["dynamic-sssbt"] = new MasaDynamicSSSBT(this);
Expand Down
1 change: 1 addition & 0 deletions src/modules/index.ts
Expand Up @@ -6,4 +6,5 @@ export * from "./sbt";
export * from "./session";
export * from "./soul-linker";
export * from "./soul-name";
export * from "./token";
export * from "./version";
2 changes: 2 additions & 0 deletions src/modules/token/index.ts
@@ -0,0 +1,2 @@
export * from "./swap";
export * from "./token";
160 changes: 160 additions & 0 deletions src/modules/token/swap.ts
@@ -0,0 +1,160 @@
import { EndpointId } from "@layerzerolabs/lz-definitions";
import { Options } from "@layerzerolabs/lz-v2-utilities";
import addressesRaw from "@masa-finance/masa-token/addresses.json";
import {
MasaToken__factory,
MasaTokenAdapter__factory,
MasaTokenOFT__factory,
} from "@masa-finance/masa-token/typechain";
import { BigNumber, ethers, utils } from "ethers";

import { Messages, SupportedNetworks } from "../../collections";
import { MasaInterface, NetworkName } from "../../interface";

const addresses = addressesRaw as Partial<{
[key in NetworkName]: { [key: string]: string };
}>;

/**
*
* @param masa
* @param to
* @param amount
* @param slippage
*/
export const swap = async (
masa: MasaInterface,
to: NetworkName,
amount: string,
slippage = 250,
) => {
const tokenAmount = BigNumber.from(utils.parseEther(amount));

// add 2,5% slippage
const tokenAmountWithSlippage = tokenAmount.add(
tokenAmount.mul(slippage).div(10000),
);

console.log(
`Swapping ${parseFloat(amount).toLocaleString()} MASA from ${masa.config.networkName} to ${to}!`,
);

// current wallet
const address = await masa.config.signer.getAddress();

const fromAddresses = addresses[masa.config.networkName];
const toAddresses = addresses[to];

const toEID = SupportedNetworks[to]?.lzEndpointId;

if (!fromAddresses || !toAddresses || !toEID) {
console.log(`Unable to swap from ${masa.config.networkName} to ${to}!`);
return;
}

// console.log(masa.config.networkName, fromAddresses, to, toAddresses);

const options = Options.newOptions()
.addExecutorLzReceiveOption(200000, 0)
.toHex()
.toString();

// console.log(utils.hexlify(utils.zeroPad(toAddresses.MasaTokenAdapter, 32)));

const sendParam: [
EndpointId,
Uint8Array,
BigNumber,
BigNumber,
string,
string,
string,
] = [
toEID, // Destination endpoint ID.
utils.zeroPad(address, 32), // Recipient address.
tokenAmountWithSlippage, // Amount to send in local decimals.
tokenAmount, // Minimum amount to send in local decimals.
options, // Additional options supplied by the caller to be used in the LayerZero message.
"0x", // The composed message for the send() operation.
"0x", // The OFT command to be executed, unused in default OFT implementations.
];

console.log(
`Tokens: ${ethers.utils.formatEther(tokenAmount)} Tokens with Slippage: ${ethers.utils.formatEther(tokenAmountWithSlippage)}`,
);

// origin
const oft = fromAddresses.MasaTokenAdapter
? MasaTokenAdapter__factory.connect(
fromAddresses.MasaTokenAdapter,
masa.config.signer,
)
: MasaTokenOFT__factory.connect(
fromAddresses.MasaTokenOFT,
masa.config.signer,
);

try {
const { quoteSend, send } = oft;

const isPeer = await oft.isPeer(
toEID,
utils.zeroPad(
toAddresses.MasaTokenAdapter ?? toAddresses.MasaTokenOFT,
32,
),
);

if (!isPeer) {
console.error("No peer found");
}

const [nativeFee] = await quoteSend(sendParam as never, false);

if (fromAddresses.MasaToken) {
const masaToken = MasaToken__factory.connect(
fromAddresses.MasaToken,
masa.config.signer,
);

const currentAllowance = await masaToken.allowance(address, oft.address);

if (currentAllowance.lt(tokenAmountWithSlippage)) {
const newAllowance = tokenAmountWithSlippage.sub(currentAllowance);

console.log(`Increasing allowance: ${utils.formatEther(newAllowance)}`);

const { wait, hash } = await masaToken.increaseAllowance(
oft.address,
newAllowance,
);

console.log(Messages.WaitingToFinalize(hash));

await wait();
}
}

console.log("Swapping ...");

const { wait, hash } = await send(
sendParam as never,
{
nativeFee,
lzTokenFee: 0,
},
address,
{
value: nativeFee,
},
);

console.log(Messages.WaitingToFinalize(hash));

await wait();
} catch (error: unknown) {
if (error instanceof Error) {
console.error(error.message);
}
}
};
8 changes: 8 additions & 0 deletions src/modules/token/token.ts
@@ -0,0 +1,8 @@
import { NetworkName } from "../../interface";
import { MasaBase } from "../../masa-base";
import { swap } from "./swap";

export class MasaToken extends MasaBase {
swap = (to: NetworkName, amount: string, slippage?: number) =>
swap(this.masa, to, amount, slippage);
}
4 changes: 3 additions & 1 deletion src/networks/addresses.ts
Expand Up @@ -2,15 +2,17 @@ import type { Addresses, NetworkName } from "../interface";
import { base, basegoerli } from "./base";
import { bsc, bsctest } from "./bsc";
import { alfajores, celo } from "./celo";
import { ethereum, goerli } from "./eth";
import { ethereum, goerli, sepolia } from "./eth";
import { opbnb, opbnbtest } from "./opbnb";
import { mumbai, polygon } from "./polygon";
import { scroll, scrollsepolia } from "./scroll";

export const addresses: Partial<{ [key in NetworkName]: Addresses }> = {
// eth
ethereum,
// @deprecated use sepolia
goerli,
sepolia,
// bsc
bsc,
bsctest,
Expand Down
4 changes: 4 additions & 0 deletions src/networks/bsc/bsctest.ts
@@ -1,9 +1,13 @@
import { bsctest as bsctestAddresses } from "@masa-finance/masa-contracts-identity/addresses.json";
import { bsctest as bsctestAddressesMasaToken } from "@masa-finance/masa-token/addresses.json";

import type { Addresses } from "../../interface";

const { SoulboundGreen: SoulboundGreenAddress } = bsctestAddresses;

export const bsctest: Addresses = {
SoulboundGreenAddress,
tokens: {
MASA: bsctestAddressesMasaToken.MasaTokenOFT,
},
};
4 changes: 2 additions & 2 deletions src/networks/eth/ethereum.ts
@@ -1,7 +1,7 @@
// identity
import { mainnet as ethereumAddresses } from "@masa-finance/masa-contracts-identity/addresses.json";
// token
import { address as MASA } from "@masa-finance/masa-token/deployments/mainnet/MasaToken.json";
import { mainnet as ethereumAddressesMasaToken } from "@masa-finance/masa-token/addresses.json";

import type { Addresses } from "../../interface";

Expand All @@ -16,7 +16,7 @@ const {

export const ethereum: Addresses = {
tokens: {
MASA,
MASA: ethereumAddressesMasaToken.MasaToken,
USDC: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
},
SoulboundIdentityAddress,
Expand Down

0 comments on commit f7e34ca

Please sign in to comment.