Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(turbo): init saveBundle to arweave via Turbo #152

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions common/protocol/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"doc": "typedoc"
},
"dependencies": {
"@ardrive/turbo-sdk": "^1.12.0-alpha.1",
"@bundlr-network/client": "^0.8.9",
"@kyvejs/sdk": "1.3.2",
"@kyvejs/types": "1.4.0",
Expand Down
120 changes: 120 additions & 0 deletions common/protocol/src/reactors/storageProviders/Turbo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { Readable } from "node:stream";

import {
privateKeyFromKyveMnemonic,
TurboAuthenticatedClient,
TurboFactory,
TurboUnauthenticatedClient,
} from "@ardrive/turbo-sdk";
import axios from "axios";

import { BundleTag, IStorageProvider } from "../../types";

type KyveMnemonic = string;
export class Turbo implements IStorageProvider {
public name = "Turbo";
public coinDecimals = 12;

private readonly valaccount: string;

constructor(valaccount: KyveMnemonic) {
if (!valaccount) {
throw new Error("Valaccount mnemonic is empty.");
}

this.valaccount = valaccount;
}

private async authenticatedTurbo(): Promise<TurboAuthenticatedClient> {
return TurboFactory.authenticated({
privateKey: await privateKeyFromKyveMnemonic(this.valaccount),
token: "kyve",
});
}

private unauthenticatedTurbo(): TurboUnauthenticatedClient {
return TurboFactory.unauthenticated({
token: "kyve",
});
}

async getAddress() {
return (await this.authenticatedTurbo()).signer.getNativeAddress();
}

async getBalance() {
const { winc } = await (await this.authenticatedTurbo()).getBalance();
return winc;
}

async getPrice(bytes: number) {
const [{ winc }] = await this.unauthenticatedTurbo().getUploadCosts({
bytes: [bytes],
});
return winc;
}

private async readableToBuffer(dataItem: Buffer | Readable) {
if (dataItem instanceof Buffer) {
return dataItem; // It's already a Buffer, so return as is.
} else if (dataItem instanceof Readable) {
// Convert Readable to Buffer
const chunks: Buffer[] = [];
for await (const chunk of dataItem) {
chunks.push(chunk);
}
return Buffer.concat(chunks);
} else {
throw new Error("dataItem must be either a Buffer or a Readable stream");
}
}

public async saveBundle(dataBuffer: Buffer, tags: BundleTag[]) {
const { id } = await (
await this.authenticatedTurbo()
).uploadFile({
fileStreamFactory: () => Readable.from(dataBuffer),
fileSizeFactory: () => dataBuffer.byteLength,
dataItemOpts: { tags },
});

return {
storageId: id,
storageData: dataBuffer,
};
}

public async saveBundleAlternative(dataBuffer: Buffer, tags: BundleTag[]) {
const turbo = await this.authenticatedTurbo();
const fileSize = dataBuffer.byteLength;

const dataItem: Buffer | Readable = (
await turbo.signer.signDataItem({
fileStreamFactory: () => Readable.from(dataBuffer),
fileSizeFactory: () => fileSize,
dataItemOpts: { tags },
})
).dataItemStreamFactory() as Buffer | Readable;

const dataItemBuffer = await this.readableToBuffer(dataItem);

const { id } = await turbo.uploadSignedDataItem({
dataItemStreamFactory: () => Readable.from(dataBuffer),
dataItemSizeFactory: () => fileSize,
});

return {
storageId: id,
storageData: dataItemBuffer, // This alternative is returning the raw data of the ANS-104 data item if needed
};
}

async retrieveBundle(storageId: string, timeout: number) {
const { data: storageData } = await axios.get(
`https://arweave.net/${storageId}`,
{ responseType: "arraybuffer", timeout }
);

return { storageId, storageData };
}
}
6 changes: 5 additions & 1 deletion common/protocol/src/reactors/storageProviders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ import { Arweave } from "./Arweave";
import { Bundlr } from "./Bundlr";
import { Kyve } from "./Kyve";
import { NoStorageProvider } from "./NoStorageProvider";
import { Turbo } from "./Turbo";

/**
* storageProviderFactory creates the correct storage provider class
* from the specified id. Current storage providers are:
*
* 0 - NoStorageProvider
* 1 - Arweave
* 2 - Bundlr
* 2 - Bundlr @deprecated
* 3 - Kyve
* 4 - Turbo
* x - NoStorageProvider (default)
*
* @method storageProviderFactory
Expand All @@ -25,6 +27,8 @@ export function storageProviderFactory(this: Validator): IStorageProvider {
return new Bundlr(this.storagePriv);
case 3:
return new Kyve(this.chainId, this.poolId, this.staker, this.valaccount);
case 4:
return new Turbo(this.valaccount);
default:
return new NoStorageProvider();
}
Expand Down
Loading