Skip to content

Commit 42ff6e4

Browse files
committed
add Token module
Signed-off-by: Eric Hegnes <eric@hegnes.com>
1 parent 62ba9da commit 42ff6e4

File tree

7 files changed

+130
-9
lines changed

7 files changed

+130
-9
lines changed

ts-sdk/src/Client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
/**
2+
3+
* TODO: - assume user specifies quote token up-front
4+
- later consider deterimning ranked quote token
25
* @since 1.0.0
36
*/
47
import type * as Context from "effect/Context"

ts-sdk/src/ClientRequest.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Option } from "effect"
1+
import type { Brand, Option } from "effect"
22
import type { Inspectable } from "effect/Inspectable"
33
import type { Pipeable } from "effect/Pipeable"
44
import type { ReadonlyRecord } from "effect/Record"
@@ -33,8 +33,8 @@ export interface ClientRequest extends Inspectable, Pipeable {
3333
readonly method: Method
3434
readonly sender: string
3535
readonly receiver: string
36-
readonly asset: string
37-
readonly quote: Option.Option<string>
36+
readonly baseToken: Token.Any
37+
readonly quoteToken: Token.Any | "auto"
3838
}
3939

4040
/**
@@ -46,6 +46,10 @@ export interface Options {
4646
readonly method?: Method | undefined
4747
/** Address to send from. */
4848
readonly sender?: string | undefined
49+
/** Base token */
50+
readonly baseToken?: string | undefined
51+
/** Quote tooken. */
52+
readonly quoteToken?: string | undefined
4953
/** Address to send from. */
5054
readonly receiver?: string | undefined
5155
/** Fee priority for gas calculation. */
@@ -71,7 +75,7 @@ export declare namespace Options {
7175
* @category models
7276
* @since 2.0.0
7377
*/
74-
export interface NoUrl extends Omit<Options, "method" | "url"> {}
78+
export interface NoQuote extends Omit<Options, "method" | "sender" | "receiver" | "quoteToken"> {}
7579
}
7680

7781
/**

ts-sdk/src/Token.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import {
2+
Effect,
3+
Either as E,
4+
flow,
5+
Match,
6+
Option as O,
7+
ParseResult,
8+
pipe,
9+
Schema as S,
10+
String as Str,
11+
} from "effect"
12+
13+
export const Erc20 = S.Struct({
14+
_tag: S.tag("Erc20"),
15+
address: S.String,
16+
})
17+
export type Erc20 = typeof Erc20.Type
18+
19+
export const CosmosBank = S.Struct({
20+
_tag: S.tag("CosmosBank"),
21+
address: S.String,
22+
})
23+
export type CosmosBank = typeof CosmosBank.Type
24+
25+
export const IbcClassic = S.Struct({
26+
_tag: S.tag("IbcClassic"),
27+
address: S.String,
28+
})
29+
export type IbcClassic = typeof IbcClassic.Type
30+
31+
export const Any = S.Union(
32+
Erc20,
33+
CosmosBank,
34+
IbcClassic,
35+
)
36+
export type Any = typeof Any.Type
37+
38+
export const TokenFromString = S.transformOrFail(
39+
S.NonEmptyString,
40+
Any,
41+
{
42+
decode: (fromA, _, ast) =>
43+
pipe(
44+
Match.value(fromA),
45+
// XXX: revise string matching
46+
Match.when(flow(Str.match(/^0x[a-fA-F0-9]{40}$/), O.isSome), (address) =>
47+
Effect.succeed(Erc20.make({ address }))),
48+
Match.when(flow(Str.match(/^[a-zA-Z0-9/.:_-]+$/), O.isSome), (address) =>
49+
Effect.succeed(CosmosBank.make({ address }))),
50+
Match.when(flow(Str.match(/^ibc\/[a-fA-F0-9]{64}$/), O.isSome), (address) =>
51+
Effect.succeed(IbcClassic.make({ address }))),
52+
Match.orElse(() =>
53+
ParseResult.fail(new ParseResult.Type(ast, fromA, "No match"))
54+
),
55+
),
56+
encode: (toI) => Effect.succeed(toI.address),
57+
},
58+
)
59+
export type TokenFromString = typeof TokenFromString.Type

ts-sdk/src/TxIncomingMessage.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
import { Data, Stream } from "effect"
1+
import { Data, Effect, Stream } from "effect"
22

3+
/**
4+
* NOTE: don't assume exhaustion
5+
* - consider documenting per ecosystem states ?
6+
*/
37
type LifecycleEvent = Data.TaggedEnum<{
48
// | { _tag: "SwitchChainStart" ; target: UniversalChainId }
59
// | { _tag: "SwitchChainDone" ; success: boolean }
@@ -11,14 +15,20 @@ type LifecycleEvent = Data.TaggedEnum<{
1115
// | { _tag: "Confirmed" ; block: bigint }
1216
// | { _tag: "Finalised" ; height: bigint ; success: boolean }
1317
// | { _tag: "Failed" ; reason: string }
14-
WriteTxStart: {}
15-
Success: {}
16-
Failure: {}
18+
Receipt: {}
19+
Indexed: {}
20+
Finalized: {}
1721
}>
1822

1923
export const LifecycleEvent = Data.taggedEnum<LifecycleEvent>()
2024

2125
export interface TxIncomingMessage<E> {
2226
/** lifecycle and chain events in temporal order */
23-
readonly events: Stream.Stream<unknown, E>
27+
readonly stream: Stream.Stream<LifecycleEvent, E>
28+
/**
29+
* - add default ucompletion handler (index)
30+
* - allow pred fn
31+
*/
32+
readonly waitFor: (pred: (a: LifecycleEvent['_tag']) => Effect.Effect<{ readonly txHash: string }, E>
33+
2434
}

ts-sdk/src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,10 @@ export * as ClientRequest from "./ClientRequest.js"
114114
* @since 2.0.0
115115
*/
116116
export * as ChannelRegistry from "./ChannelRegistry.js"
117+
118+
/**
119+
* TODO
120+
*
121+
* @since 2.0.0
122+
*/
123+
export * as Token from "./Token.js"

ts-sdk/test/Client.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ describe("Client", () => {
1111
ClientRequest.send("0x123", "bbn1abc", {
1212
batch: true,
1313
}),
14+
ClientRequest.withTokenBy(x => x.name === "WBTC"),
1415
)
1516

1617
const result = yield* client.execute(transfer)

ts-sdk/test/Token.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { assert, describe, it } from "@effect/vitest"
2+
import * as Token from "@unionlabs/sdk/Token"
3+
import { Effect, Schema as S } from "effect"
4+
5+
describe("Token", () => {
6+
describe("Erc20", () => {
7+
it("scratch", () => {
8+
const a = Token.Erc20.make({ address: "0xA0b86991C6218b36c1d19D4a2e9Eb0cE3606eB48" })
9+
assert.ok(true)
10+
})
11+
})
12+
describe("TokenFromString", () => {
13+
it.effect("transforms ERC20", () =>
14+
Effect.gen(function*() {
15+
const a = yield* S.decode(Token.TokenFromString)(
16+
"0xA0b86991C6218b36c1d19D4a2e9Eb0cE3606eB48",
17+
)
18+
assert.equal(a._tag, "Erc20")
19+
}))
20+
21+
it.effect("transforms Cosmos Bank", () =>
22+
Effect.gen(function*() {
23+
const a = yield* S.decode(Token.TokenFromString)(
24+
"uatom",
25+
)
26+
assert.equal(a._tag, "CosmosBank")
27+
}))
28+
29+
it.effect("transforms IBC (classic)", () =>
30+
Effect.gen(function*() {
31+
const a = yield* S.decode(Token.TokenFromString)(
32+
"ibc/B3504F0B84FEF5D8817A5196E19A886F81606DCD9D9FCA2E01B7F38379F94303",
33+
)
34+
assert.equal(a._tag, "CosmosBank")
35+
}))
36+
})
37+
})

0 commit comments

Comments
 (0)