Skip to content

Commit

Permalink
feat(context-module): new Nft context loader for ERC1155 and ERC721
Browse files Browse the repository at this point in the history
  • Loading branch information
aussedatlo committed May 3, 2024
1 parent 34dad0e commit 0938823
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 3 deletions.
120 changes: 120 additions & 0 deletions libs/ledgerjs/packages/context-module/src/loaders/NftLoader.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import axios from "axios";
import { LoaderOptions } from "../models/LoaderOptions";
import { Transaction } from "../models/Transaction";
import { NftLoader } from "./NftLoader";

describe("NftLoader", () => {
let loader: NftLoader;

beforeEach(() => {
jest.restoreAllMocks();
loader = new NftLoader();
});

describe("load function", () => {
it("should return an empty array if no dest", async () => {
const options = {} as LoaderOptions;
const transaction = { to: undefined, data: "0x01" } as Transaction;

const result = await loader.load(transaction, options);

expect(result).toEqual([]);
});

it("should return an empty array if undefined data", async () => {
const options = {} as LoaderOptions;
const transaction = {
to: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
data: undefined,
} as unknown as Transaction;

const result = await loader.load(transaction, options);

expect(result).toEqual([]);
});

it("should return an empty array if empty data", async () => {
const options = {} as LoaderOptions;
const transaction = {
to: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
data: "0x",
} as unknown as Transaction;

const result = await loader.load(transaction, options);

expect(result).toEqual([]);
});

it("should return an empty array if selector not supported", async () => {
const options = {} as LoaderOptions;
const transaction = {
to: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
data: "0x095ea7b20000000000000",
} as unknown as Transaction;

const result = await loader.load(transaction, options);

expect(result).toEqual([]);
});

it("should return an error when no plugin response", async () => {
const options = {} as LoaderOptions;
const transaction = {
to: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
data: "0x095ea7b30000000000000",
} as unknown as Transaction;
jest.spyOn(axios, "request").mockResolvedValueOnce({ data: {} });

const result = await loader.load(transaction, options);

expect(result).toEqual([
expect.objectContaining({
type: "error" as const,
error: new Error("[ContextModule] NftLoader: unexpected empty response"),
}),
]);
});

it("should return an error when no nft data response", async () => {
const options = {} as LoaderOptions;
const transaction = {
to: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
data: "0x095ea7b30000000000000",
} as unknown as Transaction;
jest.spyOn(axios, "request").mockResolvedValueOnce({ data: { payload: "payload1" } });
jest.spyOn(axios, "request").mockResolvedValueOnce({ data: {} });

const result = await loader.load(transaction, options);

expect(result).toEqual([
expect.objectContaining({
type: "error" as const,
error: new Error("[ContextModule] NftLoader: no nft metadata"),
}),
]);
});

it("should return a response", async () => {
const options = {} as LoaderOptions;
const transaction = {
to: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
data: "0x095ea7b30000000000000",
} as unknown as Transaction;
jest.spyOn(axios, "request").mockResolvedValueOnce({ data: { payload: "payload1" } });
jest.spyOn(axios, "request").mockResolvedValueOnce({ data: { payload: "payload2" } });

const result = await loader.load(transaction, options);

expect(result).toEqual([
{
type: "setPlugin" as const,
payload: "payload1",
},
{
type: "provideNFTInformation" as const,
payload: "payload2",
},
]);
});
});
});
73 changes: 70 additions & 3 deletions libs/ledgerjs/packages/context-module/src/loaders/NftLoader.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,79 @@
import axios from "axios";
import { LoaderOptions } from "../models/LoaderOptions";
import { Transaction } from "../models/Transaction";
import { ContextLoader } from "./ContextLoader";
import { ContextResponse } from "../models/ContextResponse";

enum ERC721_SUPPORTED_SELECTOR {
Approve = "0x095ea7b3",
SetApprovalForAll = "0xa22cb465",
TransferFrom = "0x23b872dd",
SafeTransferFrom = "0x42842e0e",
SafeTransferFromWithData = "0xb88d4fde",
}

enum ERC1155_SUPPORTED_SELECTOR {
SetApprovalForAll = "0xa22cb465",
SafeTransferFrom = "0xf242432a",
SafeBatchTransferFrom = "0x2eb2c2d6",
}

const SUPPORTED_SELECTORS: `0x${string}`[] = [
...Object.values(ERC721_SUPPORTED_SELECTOR),
...Object.values(ERC1155_SUPPORTED_SELECTOR),
];

export class NftLoader implements ContextLoader {
constructor() {}

load(_transaction: Transaction, _options: LoaderOptions) {
// TODO: implementation
return Promise.resolve([]);
async load(transaction: Transaction, _options: LoaderOptions) {
const responses: ContextResponse[] = [];

if (!transaction.to || !transaction.data || transaction.data === "0x") {
return [];
}

const selector = transaction.data.slice(0, 10) as `0x${string}`;

if (!this.isSelectorSupported(selector)) {
return [];
}

// EXAMPLE:
// https://nft.api.live.ledger.com/v1/ethereum/1/contracts/0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D/plugin-selector/0x095ea7b3
const pluginResponse = await axios.request<{ payload: string }>({
method: "GET",
url: `https://nft.api.live.ledger.com/v1/ethereum/${transaction.chainId}/contracts/${transaction.to}/plugin-selector/${selector}`,
});

if (!pluginResponse || !pluginResponse.data.payload) {
return [
{
type: "error" as const,
error: new Error("[ContextModule] NftLoader: unexpected empty response"),
},
];
}

responses.push({ type: "setPlugin", payload: pluginResponse.data.payload });

const nftInfoResponse = await axios.request<{ payload: string }>({
method: "GET",
url: `https://nft.api.live.ledger.com/v1/ethereum/${transaction.chainId}/contracts/${transaction.to}`,
});

if (!nftInfoResponse || !nftInfoResponse.data.payload) {
return [
{ type: "error" as const, error: new Error("[ContextModule] NftLoader: no nft metadata") },
];
}

responses.push({ type: "provideNFTInformation", payload: nftInfoResponse.data.payload });

return responses;
}

private isSelectorSupported(selector: `0x${string}`) {
return Object.values(SUPPORTED_SELECTORS).includes(selector);
}
}

0 comments on commit 0938823

Please sign in to comment.