UNPKG

@tiplink/api

Version:

Api for creating and sending TipLinks

881 lines (880 loc) 40.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Mint = exports.DataHost = exports.Dispenser = exports.Campaign = exports.TipLinkClient = exports.ON_CHAIN_SYMBOL_CHAR_LIMIT = exports.ON_CHAIN_NAME_CHAR_LIMIT = exports.decrypt = void 0; const nanoid_1 = require("nanoid"); const web3_js_1 = require("@solana/web3.js"); const index_1 = require("./index"); const themes_1 = require("./lib/themes"); const crypto_1 = require("./crypto"); Object.defineProperty(exports, "decrypt", { enumerable: true, get: function () { return crypto_1.decrypt; } }); exports.ON_CHAIN_NAME_CHAR_LIMIT = 32; exports.ON_CHAIN_SYMBOL_CHAR_LIMIT = 10; const URL_BASE = index_1.TIPLINK_ORIGIN; const API_URL_BASE = `${URL_BASE}/api`; const STEP = 100; // TODO harmonize constants with main const FAUCET_ID_LENGTH = 12; class TipLinkClient { constructor(apiKey, version = 1) { this.apiKey = apiKey; this.version = version; this.campaigns = new CampaignActions({ client: this }); this.mints = new MintActions({ client: this }); } static init(apiKey, version = 1) { return __awaiter(this, void 0, void 0, function* () { const client = new TipLinkClient(apiKey, version); try { const apiKeyRes = yield client.fetch("api_key"); client.id = apiKeyRes["account"]["id"]; client.publicKey = apiKeyRes["account"]["public_key"]; } catch (err) { throw Error("Api_key error: retriving account public_key encryption info"); } return client; }); } // TODO type return? fetch(endpoint, args = null, body = null, verb = "GET") { return __awaiter(this, void 0, void 0, function* () { const url = new URL(endpoint, `${API_URL_BASE}/v${this.version}/`); if (args !== null) { Object.entries(args).forEach(([key, value]) => { url.searchParams.append(key, value); }); } const params = { method: verb, headers: { Authorization: `Bearer ${this.apiKey}`, }, }; if (body !== null) { if (body instanceof FormData) { params.body = body; } else { // json body params.headers["Content-Type"] = "application/json"; params.body = JSON.stringify(body); } } try { const result = yield fetch(url.toString(), params); return yield result.json(); } catch (err) { console.error(err); // TODO how should we handle errors throw Error(`Api Fetch Error: ${verb} ${endpoint}`); } }); } } exports.TipLinkClient = TipLinkClient; class TipLinkApi { constructor(client) { this.client = client; } } // TODO better typing with prisma across repo or should we just not include all event types? var EventType; (function (EventType) { EventType["CREATED"] = "CREATED"; EventType["ACCESSED"] = "ACCESSED"; EventType["CLAIMED"] = "CLAIMED"; EventType["CLAIMED_BACK"] = "CLAIMED_BACK"; })(EventType || (EventType = {})); var Rate; (function (Rate) { Rate[Rate["DAILY"] = 0] = "DAILY"; Rate[Rate["WEEKLY"] = 1] = "WEEKLY"; Rate[Rate["MONTHLY"] = 2] = "MONTHLY"; Rate[Rate["YEARLY"] = 3] = "YEARLY"; Rate[Rate["FOREVER"] = 4] = "FOREVER"; Rate[Rate["MILLISECOND"] = 5] = "MILLISECOND"; })(Rate || (Rate = {})); class CampaignActions extends TipLinkApi { constructor(params) { super(params.client); } create(params) { return __awaiter(this, void 0, void 0, function* () { const name = typeof params != "undefined" && typeof params.name != "undefined" ? params.name : ""; const description = typeof params != "undefined" && typeof params.description != "undefined" ? params.description : ""; const imageUrl = typeof params != "undefined" && typeof params.imageUrl != "undefined" ? params.imageUrl : ""; const active = typeof params != "undefined" && typeof params.active != "undefined" ? params.active : true; const themeId = typeof params != "undefined" && typeof params.themeId != "undefined" ? params.themeId : null; const salt = (0, crypto_1.generateRandomSalt)(); const ck = yield (0, crypto_1.generateKey)(); const campaignData = { name: name, description: description, encryption_salt: salt, image_url: imageUrl, active: active, theme_id: themeId, }; const res = yield this.client.fetch("campaigns", null, campaignData, "POST"); const campaign = new Campaign({ client: this.client, id: res["id"], name: res["name"], description: res["description"], imageUrl: res["image_url"], active: res["active"], themeId: res["theme_id"], }); if (typeof this.client.publicKey === "undefined") { // TODO should we handle this differently throw "Unable to do server handshake with encryption key"; } else { const encryptedCampaignKey = yield (0, crypto_1.encryptPublicKey)(ck, this.client.publicKey); const keyData = { campaign_id: campaign.id, account_id: this.client.id, is_owner: true, encrypted_campaign_key: encryptedCampaignKey, }; yield this.client.fetch(`campaigns/${campaign.id}/campaign_account_joins`, null, keyData, "POST"); if (salt !== res["encryption_salt"]) { console.error("encryption salt mismatch"); } campaign.encryptionKey = ck; } campaign.encryptionSalt = res["encryption_salt"]; return campaign; }); } find(params) { return __awaiter(this, void 0, void 0, function* () { const findParams = Object.assign(Object.assign({}, params), { limit: 1, sorting: "-created_at" }); const res = (yield this.client.fetch("campaigns", findParams, null, "GET"))[0]; const campaign = new Campaign({ client: this.client, id: res["id"], name: res["name"], description: res["description"], imageUrl: res["image_url"], active: res["active"], }); campaign.encryptionSalt = res["encryption_salt"]; console.warn("unable to acquire decryption key"); // const encryptedKeyRes = await this.client.fetch(`campaigns/${campaign.id}/campaign_account_joins`); // // TODO figure out how api keys will do key exchange / sharing to get privateKey // const decryptedCampaignKey = await decryptPrivateKey( // encryptedKeyRes.encrypted_campaign_key, // privateKey // ); // campaign.encryptionKey = decryptedCampaignKey; return campaign; }); } all(params) { return __awaiter(this, void 0, void 0, function* () { const filterParams = Object.assign({}, params); const campaignResults = (yield this.client.fetch("campaigns", filterParams, null, "GET")); const campaigns = campaignResults.map((res) => { const campaign = new Campaign({ client: this.client, id: res["id"], name: res["name"], description: res["description"], imageUrl: res["image_url"], active: res["active"], }); campaign.encryptionSalt = res["encryption_salt"]; console.warn("unable to acquire decryption key"); // const encryptedKeyRes = await this.client.fetch(`campaigns/${campaign.id}/campaign_account_joins`); // // TODO figure out how api keys will do key exchange / sharing to get privateKey // const decryptedCampaignKey = await decryptPrivateKey( // encryptedKeyRes.encrypted_campaign_key, // privateKey // ); // campaign.encryptionKey = decryptedCampaignKey; return campaign; }); return campaigns; }); } } class Campaign extends TipLinkApi { constructor(params) { super(params.client); this.id = params.id; this.name = params.name; this.description = params.description; this.imageUrl = params.imageUrl; this.active = params.active; this.themeId = params.themeId; this.dispensers = new DispenserActions({ client: this.client, campaign: this, }); } addEntries(tiplinks) { return __awaiter(this, void 0, void 0, function* () { const tiplinkToCampaignEntry = (tiplink) => __awaiter(this, void 0, void 0, function* () { if (!this.encryptionKey || !this.encryptionSalt) { throw "No Encryption Key Set"; } const encryptedLink = yield (0, crypto_1.encrypt)(tiplink.url.toString(), this.encryptionKey, this.encryptionSalt); const publicKey = tiplink.keypair.publicKey.toString(); const result = { public_key: publicKey, encrypted_link: encryptedLink, funding_txn: "funded", }; return result; }); while (tiplinks.length) { const entries = yield Promise.all(tiplinks.splice(-1 * STEP).map(tiplinkToCampaignEntry)); yield this.client.fetch(`campaigns/${this.id}/campaign_entries`, null, entries, "POST"); const analytics = entries.map((entry) => { return { event: "CREATED", public_key: entry.public_key, }; }); yield this.client.fetch("analytics", null, analytics, "POST"); } return true; }); } hideEntries(tiplinks) { return __awaiter(this, void 0, void 0, function* () { const publicKeys = tiplinks.map((tp) => tp instanceof index_1.TipLink ? tp.keypair.publicKey : tp); const entries = { publicKeys, funding_txn: "", }; yield this.client.fetch(`campaigns/${this.id}/campaign_entries`, null, entries, "PUT"); const analytics = publicKeys.map((pk) => { return { event: "FUNDING_FAILED", public_key: pk, }; }); yield this.client.fetch("analytics", null, analytics, "POST"); return true; }); } getEntries(params) { return __awaiter(this, void 0, void 0, function* () { const entryToTipLink = (entry) => __awaiter(this, void 0, void 0, function* () { if (typeof this.encryptionKey == "undefined" || typeof this.encryptionSalt == "undefined") { console.warn("No Decryption Key: Unable to decrypt entries"); return null; } let link = ""; if (entry.encrypted_link !== null) { link = yield (0, crypto_1.decrypt)(entry.encrypted_link, this.encryptionKey, this.encryptionSalt); const tl = (0, themes_1.attachTheme)(yield index_1.TipLink.fromLink(link), this.themeId); return tl; } return null; }); const resEntries = yield this.client.fetch(`campaigns/${this.id}/campaign_entries`, params); let entries = []; while (resEntries.length) { const entry = yield Promise.all(resEntries.splice(-1 * STEP).map(entryToTipLink)); entries = entries.concat(entry); } // TODO include analytics? and id give whole entry object? return entries; }); } getAnalytic(publicKey) { return __awaiter(this, void 0, void 0, function* () { const analyticsRes = yield this.client.fetch(`campaigns/${this.id}/analytics`, { public_key: publicKey, }); let analytic = null; analyticsRes.forEach((res) => { // TODO should we display most recent created_at in category? if (res.event == "CLAIMED" || res.event == "TRANSFERED" || res.event == "RECREATED" || res.event == "WITHDRAWN") { analytic = { public_key: new web3_js_1.PublicKey(res.public_key), event: EventType.CLAIMED, created_at: new Date(res.created_at), }; } else if (res.event == "CLAIMED_BACK" && (!analytic || analytic.event !== EventType.CLAIMED)) { analytic = { public_key: new web3_js_1.PublicKey(res.public_key), event: EventType.CLAIMED_BACK, created_at: new Date(res.created_at), }; } else if (res.event == "ACCESSED" && (!analytic || (analytic.event !== EventType.CLAIMED && analytic.event !== EventType.CLAIMED_BACK))) { analytic = { public_key: new web3_js_1.PublicKey(res.public_key), event: EventType.ACCESSED, created_at: new Date(res.created_at), }; } else if (res.event == "CREATED" && (!analytic || (analytic.event !== EventType.CLAIMED && analytic.event !== EventType.CLAIMED_BACK && analytic.event !== EventType.ACCESSED))) { analytic = { public_key: new web3_js_1.PublicKey(res.public_key), event: EventType.CREATED, created_at: new Date(res.created_at), }; } }); return analytic; }); } getAnalytics() { return __awaiter(this, void 0, void 0, function* () { // TODO clean up response here and type const analyticsRes = yield this.client.fetch(`campaigns/${this.id}/analytics_summary`); return analyticsRes; }); } share(email, admin = false) { return __awaiter(this, void 0, void 0, function* () { const accounts = yield this.client.fetch(`accounts_public`, { torus_id: email, }); const account = accounts[0]; if (!account) { console.warn("invalid email"); return false; } const campaignAccounts = yield this.client.fetch(`campaigns/${this.id}/campaign_account_joins`, { account_id: account.id, }, null, "GET"); const joinEntry = campaignAccounts.find((csa) => csa.account.torus_id === email); if (joinEntry) { console.warn("already shared with this email"); if (joinEntry.is_owner === admin) { return false; } } let encryptedCampaignKey = ""; if (typeof this.encryptionKey === "undefined") { console.warn("encryptionKey not set: sharing view only"); } else { encryptedCampaignKey = yield (0, crypto_1.encryptPublicKey)(this.encryptionKey, account.public_key); } const shareParams = { campaign_id: this.id, account_id: account.id, encrypted_campaign_key: encryptedCampaignKey, is_owner: admin, }; const shareResp = yield this.client.fetch(`campaigns/${this.id}/campaign_account_joins${joinEntry ? `/${joinEntry.id}` : ""}`, null, shareParams, joinEntry ? "PUT" : "POST"); return !!shareResp; }); } } exports.Campaign = Campaign; class DispenserActions extends TipLinkApi { constructor(params) { super(params.client); this.campaign = params.campaign; } create(params) { return __awaiter(this, void 0, void 0, function* () { const useCaptcha = typeof params != "undefined" && typeof params.useCaptcha != "undefined" ? params.useCaptcha : true; const useFingerprint = typeof params != "undefined" && typeof params.useFingerprint != "undefined" ? params.useFingerprint : true; const unlimitedClaims = typeof params != "undefined" && typeof params.unlimitedClaims != "undefined" ? params.unlimitedClaims : false; const active = typeof params != "undefined" && typeof params.active != "undefined" && params.active !== null ? params.active : true; const includedEntryIds = typeof params != "undefined" && typeof params.includedEntryIds != "undefined" && params.includedEntryIds !== null ? params.includedEntryIds : []; const excludedEntryIds = typeof params != "undefined" && typeof params.excludedEntryIds != "undefined" && params.excludedEntryIds !== null ? params.excludedEntryIds : []; const rateLimits = yield this.client.fetch(`campaigns/${this.campaign.id}/rate_limits`); yield Promise.all(rateLimits.map((rateLimit) => __awaiter(this, void 0, void 0, function* () { const deleteRateLimitRes = yield this.client.fetch(`campaigns/${this.campaign.id}/rate_limits/${rateLimit["id"]}`, null, null, "DELETE"); return deleteRateLimitRes; }))); if (!unlimitedClaims) { const rateLimitData = { rate: "FOREVER", rate_multiple: 1, claims: 1, // "rate_type": "user", }; yield this.client.fetch(`campaigns/${this.campaign.id}/rate_limits`, null, rateLimitData, "POST"); } const faucetData = { active: active, name: "faucet", fingerprint: useFingerprint, recaptcha: useCaptcha, }; const faucet = yield this.client.fetch(`campaigns/${this.campaign.id}/faucet`, null, faucetData, "POST"); const dispenser = new Dispenser({ client: this.client, campaign: this.campaign, id: faucet["id"], urlSlug: faucet["url_slug"], useCaptcha: faucet["recaptcha"], useFingerprint: faucet["fingerprint"], unlimitedClaims: unlimitedClaims, active: faucet["active"], }); const faucetEntryData = { all: includedEntryIds.length === 0, included_campaign_entry_ids: includedEntryIds, excluded_campaign_entry_ids: excludedEntryIds, }; yield this.client.fetch(`campaigns/${this.campaign.id}/faucet/${faucet.id}/faucet_entries`, null, faucetEntryData, "POST"); return dispenser; }); } find(params) { return __awaiter(this, void 0, void 0, function* () { const findParams = Object.assign(Object.assign({}, params), { limit: 1, sorting: "-created_at" }); const faucet = (yield this.client.fetch(`campaigns/${this.campaign.id}/faucet`, findParams, null, "GET"))[0]; // TODO determine unlimited claims properly const dispenser = new Dispenser({ client: this.client, campaign: this.campaign, id: faucet["id"], urlSlug: faucet["url_slug"], useCaptcha: faucet["recaptcha"], useFingerprint: faucet["fingerprint"], unlimitedClaims: false, active: faucet["active"], }); return dispenser; }); } all(params) { return __awaiter(this, void 0, void 0, function* () { const filterParams = Object.assign({}, params); const faucets = (yield this.client.fetch(`campaigns/${this.campaign.id}/faucet`, filterParams, null, "GET")); // TODO determine unlimited claims properly const dispensers = faucets.map((faucet) => { const dispenser = new Dispenser({ client: this.client, campaign: this.campaign, id: faucet["id"], urlSlug: faucet["url_slug"], useCaptcha: faucet["recaptcha"], useFingerprint: faucet["fingerprint"], unlimitedClaims: false, active: faucet["active"], }); return dispenser; }); return dispensers; }); } } class Dispenser extends TipLinkApi { constructor(params) { super(params.client); this.campaign = params.campaign; this.urlSlug = params.urlSlug; this.useFingerprint = params.useFingerprint; this.unlimitedClaims = params.unlimitedClaims; this.useCaptcha = params.useCaptcha; this.active = params.active; this.id = params.id; this.url = this.getUrl(); } getUrl() { if (typeof this.campaign.encryptionKey == "undefined") { throw "invalid dispenser no decryption key available"; } const urlString = `${URL_BASE}/f/${this.campaign.id}-${this.urlSlug}#${this.campaign.encryptionKey}`; return new URL(urlString); } pause() { return __awaiter(this, void 0, void 0, function* () { const data = { active: false, }; const faucet = yield this.client.fetch(`campaigns/${this.campaign.id}/faucet/${this.id}`, null, data, "PUT"); this.active = faucet.active; return true; }); } resume() { return __awaiter(this, void 0, void 0, function* () { const data = { active: true, }; const faucet = yield this.client.fetch(`campaigns/${this.campaign.id}/faucet/${this.id}`, null, data, "PUT"); this.active = faucet.active; return true; }); } refresh() { return __awaiter(this, void 0, void 0, function* () { const data = { url_slug: (0, nanoid_1.nanoid)(FAUCET_ID_LENGTH), }; const faucet = yield this.client.fetch(`campaigns/${this.campaign.id}/faucet/${this.id}`, null, data, "PUT"); this.urlSlug = faucet["url_slug"]; this.url = this.getUrl(); return this.url; }); } delete() { return __awaiter(this, void 0, void 0, function* () { yield this.client.fetch(`campaigns/${this.campaign.id}/faucet/${this.id}`, null, null, "DELETE"); return true; }); } } exports.Dispenser = Dispenser; var DataHost; (function (DataHost) { DataHost["Arweave"] = "arweave"; DataHost["Shadow"] = "shadow"; })(DataHost = exports.DataHost || (exports.DataHost = {})); const IMAGE_HOST_DEFAULT = DataHost.Arweave; class MintActions extends TipLinkApi { constructor(params) { super(params.client); } transformAttributes(attributes) { const newMintAttributes = []; if (Object.keys(attributes).length > 0) { Object.keys(attributes).forEach((key) => { newMintAttributes.push({ trait_type: key, value: attributes[key] }); }); } return newMintAttributes; } isValidUrl(urlString) { const url = new URL(urlString); return url.protocol === "https:" || url.protocol === "http:"; } getFees(params) { return __awaiter(this, void 0, void 0, function* () { const costRequest = { metadata: { name: params.mintName, symbol: params.symbol, creator: params.creatorPublicKey.toBase58(), royalties: Number(params.royalties), description: params.mintDescription, attributes: this.transformAttributes(params.attributes || {}), externalUrl: params.externalUrl, image: params.mintImageUrl, mimeType: "image/png", }, imageSize: 0, supply: params.mintLimit, collectionMint: !!params.existingCollectionId, imageHost: params.dataHost || IMAGE_HOST_DEFAULT, }; const costData = (yield this.client.fetch("/api/dynamic_mint/calculate_campaign_costs", null, costRequest, "POST"))[0]; // TODO type this response? let total = 0; Object.keys(costData).forEach((costKey) => (total += costData[costKey].cost || 0)); const destination = new web3_js_1.PublicKey(costData.publicKeyToFund); return { publicKey: destination, feeLamports: total * web3_js_1.LAMPORTS_PER_SOL, }; }); } create(params) { return __awaiter(this, void 0, void 0, function* () { const formData = new FormData(); const index = 0; if (params.mintName.length > exports.ON_CHAIN_NAME_CHAR_LIMIT) { throw Error("Mint Name too Long"); } else if (params.symbol.length > exports.ON_CHAIN_SYMBOL_CHAR_LIMIT) { throw Error("Mint Symbol too Long"); } else if (typeof params.royalties !== "undefined" && (params.royalties > 50 || params.royalties < 0)) { throw Error("Royalties must be between 0 and 50%"); } else if (typeof params.externalUrl !== "undefined" && params.externalUrl !== "" && !this.isValidUrl(params.externalUrl)) { throw Error("Invalid external url"); } if (!params.feeTransactionHash) { throw Error("Missing feeTransactionHash"); } if (params.campaignName) { formData.append(`mint[${index}][campaignName]`, params.campaignName); } if (params.campaignDescription) { formData.append(`mint[${index}][campaignDescription]`, params.campaignDescription); } if (params.themeId) { formData.append(`mint[${index}][themeId]`, String(params.themeId)); } if (params.mintName) { formData.append(`mint[${index}][name]`, params.mintName); } if (params.symbol) { formData.append(`mint[${index}][symbol]`, params.symbol); } if (params.mintDescription) { formData.append(`mint[${index}][description]`, params.mintDescription); } if (params.mintImageUrl) { formData.append(`mint[${index}][imageUrl]`, params.mintImageUrl); } if (params.dataHost) { formData.append(`mint[${index}][imageHost]`, params.dataHost || IMAGE_HOST_DEFAULT); } if (params.mintImageUrl) { formData.append(`mint[${index}][archiveImageUrl]`, params.mintImageUrl); } if (params.externalUrl) { formData.append(`mint[${index}][externalUrl]`, params.externalUrl); } if (params.existingCollectionId) { formData.append(`mint[${index}][collectionMint]`, params.existingCollectionId); } if (params.mintLimit.toString()) { formData.append(`mint[${index}][initialLimit]`, params.mintLimit.toString()); } if (params.creatorPublicKey) { formData.append(`mint[${index}][creator]`, params.creatorPublicKey.toBase58() || ""); } if (params.royalties) { formData.append(`mint[${index}][sellerFeeBasisPoints]`, JSON.stringify(Number(params.royalties) * 100)); } if (params.royaltiesDestination) { formData.append(`mint[${index}][royaltiesDestination]`, params.royaltiesDestination.toBase58() || ""); } if (params.attributes) { formData.append(`mint[${index}][attributes]`, JSON.stringify(this.transformAttributes(params.attributes))); } if (formData.get(`mint[${index}][campaignName]`) === null) { throw Error("campaignName is required"); } else if (formData.get(`mint[${index}][name]`) === null) { throw Error("mintName is required"); } else if (formData.get(`mint[${index}][archiveImageUrl]`) === null) { throw Error("imageUrl is required"); // TODO upload image too? } else if (formData.get(`mint[${index}][symbol]`) === null) { throw Error("symbol is required"); } else if (formData.get(`mint[${index}][initialLimit]`) === null) { throw Error("mintLimit is required"); } if (!params.feeTransactionHash) { throw Error("feeTransactionHash is required"); } const stageResponse = (yield this.client.fetch("/api/dynamic_mint/stage_mint_campaign", null, formData, "POST"))[0]; const feeTransactionHash = params.feeTransactionHash; const createResponse = (yield this.client.fetch("/api/dynamic_mint/create_mint_campaign", null, { campaignIds: [stageResponse.campaign_id], transactions: [feeTransactionHash], }, "POST"))[0]; // TODO type this response const mintParams = { client: this.client, id: Number(createResponse["id"]), campaign_id: createResponse["campaign_id"], mintName: createResponse["name"], symbol: createResponse["symbol"], mintDescription: createResponse["description"], campaignName: params.campaignName, collectionId: createResponse["collection_mint"], treeAddress: createResponse["tree_address"], jsonUri: createResponse["json_uri"], creatorPublicKey: new web3_js_1.PublicKey(createResponse["creator"]), attributes: createResponse["attributes"], mintLimit: Number(createResponse["mint_limit"]), collectionUri: createResponse["collection_uri"], imageUrl: createResponse["image"], dataHost: createResponse["imageHost"], externalUrl: createResponse["external_url"], royalties: createResponse["seller_fee_basis_points"], primaryUrlSlug: createResponse["primary_url_slug"], rotatingUrlSlug: createResponse["rotating_url_slug"], useRotating: createResponse["use_rotating"], // rotating_seed_key: createResponse["rotating_seed_key"], rotatingTimeInterval: Number(createResponse["rotating_time_interval"]), totpWindow: createResponse["totp_window"], userClaimLimit: createResponse["user_claim_limit"], }; if (Object.prototype.hasOwnProperty.call(createResponse, "royalties_destination") && typeof createResponse["royalties_destination"] === "string") { mintParams["royaltiesDestination"] = new web3_js_1.PublicKey(createResponse["royalties_destination"]); } const mint = new Mint(mintParams); return mint; }); } find(params) { return __awaiter(this, void 0, void 0, function* () { const res = (yield this.client.fetch(`campaigns/${params.campaign_id}/mint/specs`, { campaign_id: params.campaign_id }, null, "GET")) // TODO type this ; const mintParams = { client: this.client, id: Number(res["id"]), campaign_id: res["campaign_id"], mintName: res["name"], symbol: res["symbol"], mintDescription: res["description"], campaignName: res['name'], collectionId: res["collection_mint"], treeAddress: res["tree_address"], jsonUri: res["json_uri"], creatorPublicKey: new web3_js_1.PublicKey(res["creator"]), attributes: res["attributes"], mintLimit: Number(res["mint_limit"]), collectionUri: res["collection_uri"], imageUrl: res["image"], dataHost: res["imageHost"], externalUrl: res["external_url"], royalties: res["seller_fee_basis_points"], primaryUrlSlug: res["primary_url_slug"], rotatingUrlSlug: res["rotating_url_slug"], useRotating: res["use_rotating"], // rotating_seed_key: res["rotating_seed_key"], rotatingTimeInterval: Number(res["rotating_time_interval"]), totpWindow: res["totp_window"], userClaimLimit: res["user_claim_limit"], }; if (Object.prototype.hasOwnProperty.call(res, "royalties_destination") && typeof res["royalties_destination"] === "string") { mintParams["royaltiesDestination"] = new web3_js_1.PublicKey(res["royalties_destination"]); } const mint = new Mint(mintParams); return mint; }); } } class Mint extends TipLinkApi { constructor(params) { super(params.client); this.id = params.id; this.campaign_id = params.campaign_id; this.mintName = params.mintName; this.mintDescription = params.mintDescription || ""; this.campaignName = params.campaignName; this.imageUrl = params.imageUrl; this.externalUrl = params.externalUrl || ""; this.dataHost = params.dataHost || IMAGE_HOST_DEFAULT; this.symbol = params.symbol; this.mintDescription = params.mintDescription || ""; this.mintLimit = params.mintLimit; this.attributes = params.attributes || []; this.creatorPublicKey = params.creatorPublicKey; this.collectionId = params.collectionId; this.treeAddress = params.treeAddress; this.jsonUri = params.jsonUri; this.collectionUri = params.collectionUri; this.primaryUrlSlug = params.primaryUrlSlug; this.rotatingUrlSlug = params.rotatingUrlSlug; this.useRotating = params.useRotating; this.rotatingTimeInterval = params.rotatingTimeInterval; this.totpWindow = params.totpWindow; this.userClaimLimit = params.userClaimLimit; this.royaltiesDestination = params.royaltiesDestination; this.royalties = params.royalties; } // TODO how should we handle rotating urls getMintUrl() { return new URL(`${URL_BASE}/m/${this.primaryUrlSlug}`); } getAnalytics() { return __awaiter(this, void 0, void 0, function* () { // TODO clean up response here and type const analyticsRes = yield this.client.fetch(`campaigns/${this.campaign_id}/mint/analytics/summary`); return analyticsRes; }); } getMintActivity(params) { return __awaiter(this, void 0, void 0, function* () { // TODO should this only work for non individual links? const activitiesRes = yield this.client.fetch(`campaigns/${this.campaign_id}/mint/activities`, { url_slug: params.urlSlug }); if (Object.prototype.hasOwnProperty.call(activitiesRes, 'activities') && activitiesRes.activities.length > 0) { const activity = activitiesRes.activities[0]; // @ts-ignore if (Object.prototype.hasOwnProperty.call(activity, 'mint')) { return { claim_txn: activity.mint.claim_txn, timestamp: new Date(activity.mint.timestamp), destination: new web3_js_1.PublicKey(activity.mint.destination), }; } return { claim_txn: activity.claim_txn, timestamp: new Date(activity.timestamp), destination: new web3_js_1.PublicKey(activity.destination), }; } return null; }); } share(email, admin = false) { return __awaiter(this, void 0, void 0, function* () { const accounts = yield this.client.fetch(`accounts_public`, { torus_id: email, }); const account = accounts[0]; if (!account) { console.warn("invalid email"); return false; } const campaignAccounts = yield this.client.fetch(`campaigns/${this.campaign_id}/campaign_account_joins`, { account_id: account.id, }, null, "GET"); const joinEntry = campaignAccounts.find((csa) => csa.account.torus_id === email); if (joinEntry) { console.warn("already shared with this email"); if (joinEntry.is_owner === admin) { return false; } } const shareParams = { campaign_id: this.campaign_id, account_id: account.id, encrypted_campaign_key: "", is_owner: admin, }; const shareResp = yield this.client.fetch(`campaigns/${this.campaign_id}/campaign_account_joins${joinEntry ? `/${joinEntry.id}` : ""}`, null, shareParams, joinEntry ? "PUT" : "POST"); return !!shareResp; }); } } exports.Mint = Mint;