Copy import fs from 'fs'
import fetch from 'node-fetch'
import { v4 as uuidv4 } from 'uuid'
main ( 'issuerAddress' , 'artistAddress' , 'apiKey' , 'receiverAddress' ) // Use your staging credential to test
async function main (
luwIssuer : string ,
luwArtist : string ,
apiKey : string ,
toAddress : string | undefined
) {
console .log ( 'Starting the issue API example' )
const headersCommerceApi = {
'Content-Type' : 'application/json' ,
accept : 'application/json' ,
'commerce-api-key' : apiKey ,
'issuer-address' : luwIssuer ,
}
/////////////////////////
// GENERATE SIGNED URL //
// STEP 7 - 8 //
/////////////////////////
console .log ( 'Starting to generate signed URL' )
const signedUrlEndpoint =
'https://api-stg.startrail.startbahn.jp/port/api/v1/commerce/signedUrls'
const fileName = `test- ${ uuidv4 () } .txt`
const fileWs = fs .createWriteStream (fileName)
fileWs .write ( 'test' )
fileWs .end ()
const requestBodySignedUrl = {
payload : [
{
filename : fileName ,
category : 'artwork' ,
} ,
] ,
action : 'write' ,
}
const responseSignedUrl = await fetch (signedUrlEndpoint , {
method : 'POST' ,
body : JSON .stringify (requestBodySignedUrl) ,
headers : headersCommerceApi ,
})
if ( ! responseSignedUrl .ok) {
console .log ( 'handle the error' )
}
const jsonSignedUrlResponse = await responseSignedUrl .json ()
const contentType = jsonSignedUrlResponse .results[ 0 ].contentType
// Upload the file to the Signed URL
// example using cURL
// `curl -X PUT -H 'Content-Type: text/plain' --upload-file my-file.txt '${res.url}
const signedUrl = jsonSignedUrlResponse .results[ 0 ].url
// save res.finalUrl and use it for Issue endpoint
const finalUrl = jsonSignedUrlResponse .results[ 0 ].finalUrl
/////////////////////////////
// UPLOAD USING SIGNED URL //
// STEP 9 //
/////////////////////////////
console .log ( 'Starting to upload the file' )
const fileRs = fs .createReadStream (fileName)
const responseUpload = await fetch (signedUrl , {
method : 'PUT' ,
headers : {
'Content-Type' : contentType ,
} ,
body : fileRs ,
})
if ( ! responseUpload .ok) {
console .log ( 'handle the error' )
}
///////////////////////////
// CHECK FILE READINESS //
// STEP 11 - 12 //
///////////////////////////
console .log ( 'Starting to check the file readiness' )
await sleep ( 360000 ) // wait to make sure the hash is calculated
const fileInfoEndpoint =
'https://api-stg.startrail.startbahn.jp/port/api/v1/commerce/fileMetadata'
const requestBodyFileInfo = {
payload : [
{
filename : fileName ,
category : 'artwork' ,
} ,
] ,
}
const responseFileInfo = await fetch (fileInfoEndpoint , {
method : 'POST' ,
body : JSON .stringify (requestBodyFileInfo) ,
headers : headersCommerceApi ,
})
if ( ! responseFileInfo .ok) {
console .log ( 'handle the error' )
}
const jsonFileInfoResponse = await responseFileInfo .json ()
const calculatedHash = jsonFileInfoResponse .results[ 0 ].hash
if ( ! calculatedHash) {
console .log ( 'There is error in hash calculation, contact Startbahn' )
}
//////////////////
// ISSUE SRR //
// STEP 13 - 16 //
//////////////////
console .log ( 'Starting to issuen an SRR' )
const issueEndpoint =
'https://api-stg.startrail.startbahn.jp/port/api/v1/commerce/srrs'
const singlePayload : any = {
externalId : uuidv4 () ,
artistAddress : luwArtist ,
isPrimaryIssuer : true ,
lockExternalTransfer : false ,
// If the uploaded file is thumbnail or contract terms, it should be put on the metadata
metadata : randomizeMetadata (finalUrl , finalUrl) ,
attachmentFiles : [
{
name : requestBodyFileInfo .payload[ 0 ].filename ,
// If the uploaded file is an attachment file, put it in this field
url : finalUrl ,
category : requestBodyFileInfo .payload[ 0 ].category ,
} ,
] ,
}
if (toAddress) {
singlePayload .to = toAddress
}
const requestBodyissue = {
payload : [singlePayload] ,
}
const responseIssue = await fetch (issueEndpoint , {
method : 'POST' ,
body : JSON .stringify (requestBodyissue) ,
headers : headersCommerceApi ,
})
if ( ! responseIssue .ok) {
console .log ( 'handle the error' )
}
const jsonIssueResponse = await responseIssue .json ()
console .log (jsonIssueResponse)
}
async function sleep (milliseconds : number ) : Promise < NodeJS . Timeout > {
return new Promise ((resolve) => setTimeout (resolve , milliseconds))
}
function randomizeMetadata (
thumbnailURL : string ,
contractTermsFileURL : string
) {
return {
$schema :
'https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.1.schema.json' ,
$schemaIntegrity :
'sha256-15f8e99eb9d4292287282942db2f2de9bbcc4761c555c6f7da23feec010c1221' ,
title : {
en : 'A title-' + uuidv4 () ,
ja : 'タイトル-' + uuidv4 () ,
zh : '一个标题-' + uuidv4 () ,
} ,
size : {
width : 200.0 ,
height : 400.0 ,
depth : 12.4 ,
unit : 'pixel' ,
flexibleDescription : {
en : 'flexibleDescription comes here' ,
ja : '自由だーーー' ,
} ,
} ,
medium : {
en : 'Oil on canvas' ,
ja : 'キャンバスに油彩' ,
zh : '布面油画' ,
} ,
edition : {
uniqueness : 'unique work' ,
proofType : 'ED' ,
number : 1 ,
totalNumber : 3 ,
note : {
en : 'some extra notes in 1 or more languages' ,
} ,
} ,
contractTerms : {
royaltyRate : 15.7 ,
fileURL : contractTermsFileURL ,
} ,
note : {
en : 'note' ,
zh : '注意' ,
} ,
thumbnailURL ,
yearOfCreation : {
en : 'around 2010-2020' ,
ja : '2010年から2020年頃' ,
} ,
isDigital : true ,
name : 'some nft name' ,
description : 'some nft description' ,
image :
'https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png' ,
external_url : 'https://startrail.io/' ,
}
}