Skip to content

Commit

Permalink
feat(web): implement walletAdapter for SOL and ETH web signing suppor…
Browse files Browse the repository at this point in the history
…t PE-6052
  • Loading branch information
fedellen committed Sep 30, 2024
1 parent 40692dd commit 2ab2486
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 122 deletions.
141 changes: 138 additions & 3 deletions src/common/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,36 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TurboUnauthenticatedConfiguration } from '../types.js';
import {
EthereumSigner,
HexInjectedSolanaSigner,
HexSolanaSigner,
InjectedEthereumSigner,
} from '@dha-team/arbundles';

import {
TokenType,
TurboAuthenticatedConfiguration,
TurboAuthenticatedUploadServiceConfiguration,
TurboAuthenticatedUploadServiceInterface,
TurboSigner,
TurboUnauthenticatedConfiguration,
TurboWallet,
WalletAdapter,
isEthereumWalletAdapter,
isSolanaWalletAdapter,
} from '../types.js';
import { TurboWinstonLogger } from './logger.js';
import { TurboUnauthenticatedPaymentService } from './payment.js';
import { TurboUnauthenticatedClient } from './turbo.js';
import {
TurboAuthenticatedPaymentService,
TurboUnauthenticatedPaymentService,
} from './payment.js';
import { TurboDataItemAbstractSigner } from './signer.js';
import { defaultTokenMap } from './token/index.js';
import {
TurboAuthenticatedClient,
TurboUnauthenticatedClient,
} from './turbo.js';
import { TurboUnauthenticatedUploadService } from './upload.js';

export abstract class TurboBaseFactory {
Expand All @@ -39,6 +65,8 @@ export abstract class TurboBaseFactory {
}: TurboUnauthenticatedConfiguration = {}) {
token = token === 'pol' ? 'matic' : token;

token ??= 'arweave'; // default to arweave if token is not provided

const paymentService = new TurboUnauthenticatedPaymentService({
...paymentServiceConfig,
logger: this.logger,
Expand All @@ -54,4 +82,111 @@ export abstract class TurboBaseFactory {
paymentService,
});
}

protected abstract getSigner({
providedPrivateKey,
providedSigner,
providedWalletAdapter,
logger,
token,
}: {
providedSigner: TurboSigner | undefined;
providedPrivateKey: TurboWallet | undefined;
providedWalletAdapter: WalletAdapter | undefined;
token: TokenType;
logger: TurboWinstonLogger;
}): TurboDataItemAbstractSigner;

protected abstract getAuthenticatedUploadService(
config: TurboAuthenticatedUploadServiceConfiguration,
): TurboAuthenticatedUploadServiceInterface;

protected getAuthenticatedTurbo({
privateKey,
signer: providedSigner,
paymentServiceConfig = {},
uploadServiceConfig = {},
token,
gatewayUrl,
tokenMap,
tokenTools,
logger,
walletAdapter,
}: TurboAuthenticatedConfiguration & { logger: TurboWinstonLogger }) {
token = token === 'pol' ? 'matic' : token;

if (!token) {
if (providedSigner) {
// Derive token from signer if not provided
if (providedSigner instanceof EthereumSigner) {
token = 'ethereum';
} else if (providedSigner instanceof HexSolanaSigner) {
token = 'solana';
} else {
token = 'arweave';
}
} else {
token = 'arweave';
}
}

if (walletAdapter) {
if (token === 'solana') {
if (!isSolanaWalletAdapter(walletAdapter)) {
throw new Error(
'Solana wallet adapter must implement publicKey and signMessage',
);
}
providedSigner = new HexInjectedSolanaSigner(walletAdapter);
}

if (token == 'ethereum') {
if (!isEthereumWalletAdapter(walletAdapter)) {
throw new Error('Ethereum wallet adapter must implement getSigner');
}
providedSigner = new InjectedEthereumSigner(walletAdapter);
}
throw new Error(
'Wallet adapter is currently only supported for Solana and Ethereum',
);
}

Check warning on line 152 in src/common/factory.ts

View check run for this annotation

Codecov / codecov/patch

src/common/factory.ts#L134-L152

Added lines #L134 - L152 were not covered by tests

const turboSigner = this.getSigner({
providedSigner,
providedPrivateKey: privateKey,
token,
logger,
providedWalletAdapter: walletAdapter,
});

token ??= 'arweave'; // default to arweave if token is not provided
if (!tokenTools) {
if (tokenMap && token === 'arweave') {
tokenTools = tokenMap.arweave;
}
tokenTools = defaultTokenMap[token]?.({
gatewayUrl,
logger,
});
}

const paymentService = new TurboAuthenticatedPaymentService({
...paymentServiceConfig,
signer: turboSigner,
logger,
token,
tokenTools,
});
const uploadService = this.getAuthenticatedUploadService({
...uploadServiceConfig,
signer: turboSigner,
logger,
token,
});
return new TurboAuthenticatedClient({
uploadService,
paymentService,
signer: turboSigner,
});
}
}
25 changes: 25 additions & 0 deletions src/common/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import {
TurboLogger,
TurboSignedDataItemFactory,
TurboSigner,
WalletAdapter,
isEthereumWalletAdapter,
} from '../types.js';
import {
fromB64Url,
Expand All @@ -60,15 +62,18 @@ export abstract class TurboDataItemAbstractSigner
protected logger: TurboLogger;
protected signer: TurboSigner;
protected token: TokenType;
protected walletAdapter: WalletAdapter | undefined;

constructor({
signer,
logger = TurboWinstonLogger.default,
token,
walletAdapter,
}: TurboDataItemSignerParams) {
this.logger = logger;
this.signer = signer;
this.token = token;
this.walletAdapter = walletAdapter;
}

private ownerToNativeAddress(owner: string, token: TokenType): NativeAddress {
Expand Down Expand Up @@ -125,6 +130,26 @@ export abstract class TurboDataItemAbstractSigner
amount,
gatewayUrl,
}: SendTxWithSignerParams): Promise<string> {
if (this.walletAdapter) {
if (!isEthereumWalletAdapter(this.walletAdapter)) {
throw new Error(
'Unsupported wallet adapter -- must implement getSigner',
);
}
const signer = this.walletAdapter.getSigner();
if (signer.sendTransaction === undefined) {
throw new Error(
'Unsupported wallet adapter -- getSigner must return a signer with sendTransaction API for crypto funds transfer',
);
}

const { hash } = await signer.sendTransaction({
to: target,
value: parseEther(amount.toFixed(18)),
});
return hash;
}

Check warning on line 151 in src/common/signer.ts

View check run for this annotation

Codecov / codecov/patch

src/common/signer.ts#L134-L151

Added lines #L134 - L151 were not covered by tests

if (!(this.signer instanceof EthereumSigner)) {
throw new Error(
'Only EthereumSigner is supported for sendTransaction API currently!',
Expand Down
84 changes: 26 additions & 58 deletions src/node/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,45 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EthereumSigner, HexSolanaSigner } from '@dha-team/arbundles';

import { TurboBaseFactory } from '../common/factory.js';
import { defaultTokenMap } from '../common/index.js';
import { TurboAuthenticatedPaymentService } from '../common/payment.js';
import { TurboDataItemAbstractSigner } from '../common/signer.js';
import { TurboAuthenticatedClient } from '../common/turbo.js';
import {
TokenType,
GetTurboSignerParams,
TurboAuthenticatedConfiguration,
TurboSigner,
TurboWallet,
TurboAuthenticatedUploadServiceConfiguration,
TurboAuthenticatedUploadServiceInterface,
} from '../types.js';
import { createTurboSigner } from '../utils/common.js';
import { TurboNodeSigner } from './signer.js';
import { TurboAuthenticatedUploadService } from './upload.js';

export class TurboFactory extends TurboBaseFactory {
protected static getSigner(
providedSigner: TurboSigner | undefined,
providedPrivateKey: TurboWallet | undefined,
token: TokenType,
): TurboDataItemAbstractSigner {
protected getSigner({
logger,
providedPrivateKey,
providedSigner,
providedWalletAdapter,
token,
}: GetTurboSignerParams): TurboDataItemAbstractSigner {
return new TurboNodeSigner({
signer: createTurboSigner({
signer: providedSigner,
privateKey: providedPrivateKey,
token,
}),
logger: this.logger,
logger,
token,
walletAdapter: providedWalletAdapter,
});
}

protected getAuthenticatedUploadService(
config: TurboAuthenticatedUploadServiceConfiguration,
): TurboAuthenticatedUploadServiceInterface {
// Import the TurboAuthenticatedUploadService class from the node upload module
return new TurboAuthenticatedUploadService(config);
}

static authenticated({
privateKey,
signer: providedSigner,
Expand All @@ -57,53 +62,16 @@ export class TurboFactory extends TurboBaseFactory {
gatewayUrl,
tokenTools,
}: TurboAuthenticatedConfiguration) {
token = token === 'pol' ? 'matic' : token;

if (!token) {
if (providedSigner) {
// Derive token from signer if not provided
if (providedSigner instanceof EthereumSigner) {
token = 'ethereum';
} else if (providedSigner instanceof HexSolanaSigner) {
token = 'solana';
} else {
token = 'arweave';
}
} else {
token = 'arweave';
}
}

const turboSigner = this.getSigner(providedSigner, privateKey, token);

if (!tokenTools) {
if (tokenMap && token === 'arweave') {
tokenTools = tokenMap.arweave;
}

tokenTools = defaultTokenMap[token]?.({
gatewayUrl,
logger: this.logger,
});
}

const paymentService = new TurboAuthenticatedPaymentService({
...paymentServiceConfig,
signer: turboSigner,
logger: this.logger,
return new TurboFactory().getAuthenticatedTurbo({
privateKey,
signer: providedSigner,
paymentServiceConfig,
uploadServiceConfig,
token,
tokenMap,
gatewayUrl,
tokenTools,
});
const uploadService = new TurboAuthenticatedUploadService({
...uploadServiceConfig,
signer: turboSigner,
logger: this.logger,
token,
});
return new TurboAuthenticatedClient({
uploadService,
paymentService,
signer: turboSigner,
});
}
}
Loading

0 comments on commit 2ab2486

Please sign in to comment.