@tiplink/api
Version:
Api for creating and sending TipLinks
881 lines (880 loc) • 40.1 kB
JavaScript
"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;