Skip to content

Commit

Permalink
Chain: Add Mint Functionality to Each Chain
Browse files Browse the repository at this point in the history
  • Loading branch information
imsk17 committed Feb 23, 2024
1 parent 3f91b95 commit ae8efc9
Show file tree
Hide file tree
Showing 12 changed files with 267 additions and 10 deletions.
10 changes: 10 additions & 0 deletions src/handlers/evm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ export function evmHandler({
extraArgs,
);
},
async mintNft(signer, ma) {
const minter = ERC721Royalty__factory.connect(ma.contract, signer);
return minter.mint(
await signer.getAddress(),
ma.tokenId,
ma.royalty,
ma.royaltyReceiver,
ma.uri,
);
},
getProvider() {
return provider;
},
Expand Down
15 changes: 13 additions & 2 deletions src/handlers/evm/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,26 @@ import {
Signer,
} from "ethers";
import { Bridge, BridgeStorage } from "../../contractsTypes/evm";
import { TNftChain } from "../types";
import { MintNft, TNftChain } from "../types";

export type TEvmHandler = TNftChain<
Signer,
Bridge.ClaimDataStruct,
Overrides,
ContractTransactionResponse,
Provider
>;
> &
MintNft<
Signer,
{
contract: string;
uri: string;
tokenId: bigint;
royalty: bigint;
royaltyReceiver: string;
},
ContractTransactionResponse
>;

export type TEvmParams = {
identifier: string;
Expand Down
3 changes: 3 additions & 0 deletions src/handlers/multiversx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ export function multiversxHandler({
getStorageContract() {
return storage;
},
async mintNft(_signer, _ma) {
throw new Error("unimplemented");
},
transform(input) {
return {
attrs: input.metadata,
Expand Down
18 changes: 16 additions & 2 deletions src/handlers/multiversx/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { INetworkProvider } from "@multiversx/sdk-network-providers/out/interface";
import { UserAddress } from "@multiversx/sdk-wallet/out/userAddress";
import { BridgeStorage } from "../../contractsTypes/evm";
import { TSingularNftChain } from "../types";
import { MintNft, TSingularNftChain } from "../types";

// Custom Interface because there is no such signer interface in mx-sdk.
export type TMultiversXSigner = {
Expand All @@ -27,13 +27,27 @@ export type TMultiversXClaimArgs = {
metadata: string;
};

/**
* arguments required to issue an NFT
*/
export type NftIssueArgs = {
readonly identifier: string;
readonly uris: Array<string>;
readonly name: string;
readonly quantity?: number;
readonly royalties?: number;
readonly hash?: string;
readonly attrs?: string;
};

export type TMultiversXHandler = TSingularNftChain<
TMultiversXSigner,
TMultiversXClaimArgs,
unknown,
string,
INetworkProvider
>;
> &
MintNft<TMultiversXSigner, NftIssueArgs, string>;

export type TMultiversXParams = {
provider: INetworkProvider;
Expand Down
16 changes: 16 additions & 0 deletions src/handlers/secret/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ export function secretHandler({
);
return tx;
},
mintNft(signer, ma) {
const mint = signer.tx.snip721.mint({
contract_address: ma.contractAddress,
msg: {
mint_nft: {
public_metadata: {
token_uri: ma.uri,
},
token_id: ma.tokenId,
owner: signer.address,
},
},
sender: signer.address,
});
return mint;
},
transform(input) {
return {
destination_chain: input.destinationChain,
Expand Down
11 changes: 9 additions & 2 deletions src/handlers/secret/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SecretNetworkClient, TxOptions, TxResponse } from "secretjs";
import { BridgeStorage } from "../../contractsTypes/evm";
import { TNftChain } from "../types";
import { MintNft, TNftChain } from "../types";

export type TSecretClaimArgs = {
token_id: string;
Expand All @@ -19,13 +19,20 @@ export type TSecretClaimArgs = {
fee: string;
};

export type SecretMintArgs = {
contractAddress: string;
uri: string;
tokenId: string;
};

export type TSecretHandler = TNftChain<
SecretNetworkClient,
TSecretClaimArgs,
TxOptions,
TxResponse,
SecretNetworkClient
>;
> &
MintNft<SecretNetworkClient, SecretMintArgs, TxResponse>;

export type TSecretParams = {
provider: SecretNetworkClient;
Expand Down
15 changes: 15 additions & 0 deletions src/handlers/tezos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,21 @@ export function tezosHandler({
metadata: nft.metadata,
};
},
async mintNft(signer, ma) {
Tezos.setSignerProvider(signer);
const contract = await Tezos.contract.at<NFTContractType>(ma.contract);
const tx = contract.methods
.mint([
{
amt: tas.nat(1),
to: tas.address(await signer.publicKeyHash()),
token_id: tas.nat(ma.tokenId),
token_uri: ma.uri,
},
])
.send();
return tx;
},
async claimNft(signer, data, extraArgs, sigs) {
const isTezosAddr =
validateAddress(data.source_nft_contract_address) === 3;
Expand Down
12 changes: 10 additions & 2 deletions src/handlers/tezos/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import {
} from "@taquito/taquito";

import { Signer } from "@taquito/taquito";
import BigNumber from "bignumber.js";
import { BridgeStorage } from "../../contractsTypes/evm";
import { address, mutez, nat } from "../../contractsTypes/tezos/type-aliases";
import { TSingularNftChain } from "../types";
import { MintNft, TSingularNftChain } from "../types";

export type TTezosClaimArgs = {
token_id: nat;
Expand All @@ -26,13 +27,20 @@ export type TTezosClaimArgs = {
fee: mutez;
};

export type TezosMintArgs = {
contract: string;
tokenId: BigNumber;
uri: string;
};

export type TTezosHandler = TSingularNftChain<
Signer,
TTezosClaimArgs,
Partial<SendParams>,
TransactionOperation,
TezosToolkit
>;
> &
MintNft<Signer, TezosMintArgs, TransactionOperation>;

export type TTezosParams = {
Tezos: TezosToolkit;
Expand Down
6 changes: 6 additions & 0 deletions src/handlers/ton/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "../../contractsTypes/ton/tonBridge";
import { NftCollection } from "../../contractsTypes/ton/tonNftCollection";
import { NftItem } from "../../contractsTypes/ton/tonNftContract";
import { TonNftCollection } from "./nft";
import { TTonHandler, TTonParams } from "./types";

export function raise(message: string): never {
Expand Down Expand Up @@ -249,6 +250,11 @@ export function tonHandler({
};
},
async approveNft(_signer, _tokenId, _contract) {},
async mintNft(signer, ma) {
const nft = new TonNftCollection(ma);
await nft.deploy(signer);
return;
},
async nftData(_tokenId, contract, _overrides) {
const nftItem = client.open(
NftItem.fromAddress(Address.parseFriendly(contract).address),
Expand Down
159 changes: 159 additions & 0 deletions src/handlers/ton/nft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import {
Address,
Cell,
OpenedContract,
SendMode,
Sender,
StateInit,
beginCell,
contractAddress,
internal,
} from "@ton/core";
import { KeyPair } from "@ton/crypto";
import { WalletContractV4 } from "@ton/ton";

export type collectionData = {
ownerAddress: Address;
royaltyPercent: number;
royaltyAddress: Address;
nextItemIndex: number;
collectionContentUrl: string;
commonContentUrl: string;
};

export class TonNftCollection {
private collectionData: collectionData;

constructor(collectionData: collectionData) {
this.collectionData = collectionData;
}

private createCodeCell(): Cell {
const NftCollectionCodeBoc =
"te6cckECFAEAAh8AART/APSkE/S88sgLAQIBYgkCAgEgBAMAJbyC32omh9IGmf6mpqGC3oahgsQCASAIBQIBIAcGAC209H2omh9IGmf6mpqGAovgngCOAD4AsAAvtdr9qJofSBpn+pqahg2IOhph+mH/SAYQAEO4tdMe1E0PpA0z/U1NQwECRfBNDUMdQw0HHIywcBzxbMyYAgLNDwoCASAMCwA9Ra8ARwIfAFd4AYyMsFWM8WUAT6AhPLaxLMzMlx+wCAIBIA4NABs+QB0yMsCEsoHy//J0IAAtAHIyz/4KM8WyXAgyMsBE/QA9ADLAMmAE59EGOASK3wAOhpgYC42Eit8H0gGADpj+mf9qJofSBpn+pqahhBCDSenKgpQF1HFBuvgoDoQQhUZYBWuEAIZGWCqALnixJ9AQpltQnlj+WfgOeLZMAgfYBwGyi544L5cMiS4ADxgRLgAXGBEuAB8YEYGYHgAkExIREAA8jhXU1DAQNEEwyFAFzxYTyz/MzMzJ7VTgXwSED/LwACwyNAH6QDBBRMhQBc8WE8s/zMzMye1UAKY1cAPUMI43gED0lm+lII4pBqQggQD6vpPywY/egQGTIaBTJbvy9AL6ANQwIlRLMPAGI7qTAqQC3gSSbCHis+YwMlBEQxPIUAXPFhPLP8zMzMntVABgNQLTP1MTu/LhklMTugH6ANQwKBA0WfAGjhIBpENDyFAFzxYTyz/MzMzJ7VSSXwXiN0CayQ==";
return Cell.fromBase64(NftCollectionCodeBoc);
}

private createDataCell(): Cell {
const data = this.collectionData;
const dataCell = beginCell();

dataCell.storeAddress(data.ownerAddress);
dataCell.storeUint(data.nextItemIndex, 64);
const contentCell = beginCell();

const collectionContent = encodeOffChainContent(data.collectionContentUrl);

const commonContent = beginCell();
commonContent.storeBuffer(Buffer.from(data.commonContentUrl));

contentCell.storeRef(collectionContent);
contentCell.storeRef(commonContent.asCell());
dataCell.storeRef(contentCell);
const NftItemCodeCell = Cell.fromBase64(
"te6cckECDQEAAdAAART/APSkE/S88sgLAQIBYgMCAAmhH5/gBQICzgcEAgEgBgUAHQDyMs/WM8WAc8WzMntVIAA7O1E0NM/+kAg10nCAJp/AfpA1DAQJBAj4DBwWW1tgAgEgCQgAET6RDBwuvLhTYALXDIhxwCSXwPg0NMDAXGwkl8D4PpA+kAx+gAxcdch+gAx+gAw8AIEs44UMGwiNFIyxwXy4ZUB+kDUMBAj8APgBtMf0z+CEF/MPRRSMLqOhzIQN14yQBPgMDQ0NTWCEC/LJqISuuMCXwSED/LwgCwoAcnCCEIt3FzUFyMv/UATPFhAkgEBwgBDIywVQB88WUAX6AhXLahLLH8s/Im6zlFjPFwGRMuIByQH7AAH2UTXHBfLhkfpAIfAB+kDSADH6AIIK+vCAG6EhlFMVoKHeItcLAcMAIJIGoZE24iDC//LhkiGOPoIQBRONkchQCc8WUAvPFnEkSRRURqBwgBDIywVQB88WUAX6AhXLahLLH8s/Im6zlFjPFwGRMuIByQH7ABBHlBAqN1viDACCAo41JvABghDVMnbbEDdEAG1xcIAQyMsFUAfPFlAF+gIVy2oSyx/LPyJus5RYzxcBkTLiAckB+wCTMDI04lUC8ANqhGIu",
);
dataCell.storeRef(NftItemCodeCell);
const royaltyBase = 1000;
const royaltyFactor = Math.floor(data.royaltyPercent * royaltyBase);
const royaltyCell = beginCell();
royaltyCell.storeUint(royaltyFactor, 16);
royaltyCell.storeUint(royaltyBase, 16);
royaltyCell.storeAddress(data.royaltyAddress);
dataCell.storeRef(royaltyCell);

return dataCell.endCell();
}

public get stateInit(): StateInit {
const code = this.createCodeCell();
const data = this.createDataCell();

return { code, data };
}
public get address(): Address {
return contractAddress(0, this.stateInit);
}

public async deploy(contract: Sender) {
await contract.send({
to: this.address,
init: this.stateInit,
value: 500000n,
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
});
return;
}

public async topUpBalance(
wallet: OpenedWallet,
nftAmount: number,
): Promise<number> {
const feeAmount = 0.026; // approximate value of fees for 1 transaction in our case
const seqno = await wallet.contract.getSeqno();
const amount = nftAmount * feeAmount;

await wallet.contract.sendTransfer({
seqno,
secretKey: wallet.keyPair.secretKey,
messages: [
internal({
value: amount.toString(),
to: this.address.toString({ bounceable: false }),
body: new Cell(),
}),
],
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
});

return seqno;
}
}

type OpenedWallet = {
contract: OpenedContract<WalletContractV4>;
keyPair: KeyPair;
};

export function encodeOffChainContent(content: string) {
let data = Buffer.from(content);
const offChainPrefix = Buffer.from([0x01]);
data = Buffer.concat([offChainPrefix, data]);
return makeSnakeCell(data);
}
function makeSnakeCell(data: Buffer): Cell {
const chunks = bufferToChunks(data, 127);

if (chunks.length === 0) {
return beginCell().endCell();
}

if (chunks.length === 1) {
return beginCell().storeBuffer(chunks[0]).endCell();
}

let curCell = beginCell();

for (let i = chunks.length - 1; i >= 0; i--) {
const chunk = chunks[i];

curCell.storeBuffer(chunk);

if (i - 1 >= 0) {
const nextCell = beginCell();
nextCell.storeRef(curCell);
curCell = nextCell;
}
}

return curCell.endCell();
}
function bufferToChunks(buff: Buffer, chunkSize: number) {
const chunks: Buffer[] = [];
while (buff.byteLength > 0) {
chunks.push(buff.subarray(0, chunkSize));
//biome-ignore lint/style/noParameterAssign: weird code from ton copy pasta
buff = buff.subarray(chunkSize);
}
return chunks;
}
Loading

0 comments on commit ae8efc9

Please sign in to comment.