From 451a2d7df892e12085851d3503f7bd6d36da11d0 Mon Sep 17 00:00:00 2001 From: defi-moses Date: Thu, 10 Oct 2024 15:03:33 +0100 Subject: [PATCH] explorer backend update --- services/explorer/api/server_test.go | 49 ++++----- .../consumer/parser/tokendata/cache.go | 5 + services/explorer/graphql/client/client.go | 50 ++++----- .../graphql/client/queries/queries.graphql | 1 + .../graphql/server/graph/model/models_gen.go | 49 ++++----- .../explorer/graphql/server/graph/partials.go | 6 ++ .../graphql/server/graph/resolver/server.go | 102 +++++++++++++----- .../graphql/server/graph/schema/types.graphql | 1 + services/explorer/static/chainIDs.yaml | 1 + .../explorer/static/tokenIDToCoinGeckoID.yaml | 1 + .../static/tokenSymbolToCoinGeckoID.yaml | 1 + .../explorer/static/tokenSymbolToTokenID.yaml | 1 + 12 files changed, 171 insertions(+), 96 deletions(-) diff --git a/services/explorer/api/server_test.go b/services/explorer/api/server_test.go index 6a5a0b27df..1125854ecb 100644 --- a/services/explorer/api/server_test.go +++ b/services/explorer/api/server_test.go @@ -35,30 +35,31 @@ func TestHandleJSONDailyStat(t *testing.T) { // nolint valueStruct := gqlClient.GetDailyStatisticsByChain{ Response: []*struct { - Date *string "json:\"date\" graphql:\"date\"" - Ethereum *float64 "json:\"ethereum\" graphql:\"ethereum\"" - Optimism *float64 "json:\"optimism\" graphql:\"optimism\"" - Cronos *float64 "json:\"cronos\" graphql:\"cronos\"" - Bsc *float64 "json:\"bsc\" graphql:\"bsc\"" - Polygon *float64 "json:\"polygon\" graphql:\"polygon\"" - Fantom *float64 "json:\"fantom\" graphql:\"fantom\"" - Boba *float64 "json:\"boba\" graphql:\"boba\"" - Metis *float64 "json:\"metis\" graphql:\"metis\"" - Moonbeam *float64 "json:\"moonbeam\" graphql:\"moonbeam\"" - Moonriver *float64 "json:\"moonriver\" graphql:\"moonriver\"" - Klaytn *float64 "json:\"klaytn\" graphql:\"klaytn\"" - Arbitrum *float64 "json:\"arbitrum\" graphql:\"arbitrum\"" - Avalanche *float64 "json:\"avalanche\" graphql:\"avalanche\"" - Dfk *float64 "json:\"dfk\" graphql:\"dfk\"" - Aurora *float64 "json:\"aurora\" graphql:\"aurora\"" - Harmony *float64 "json:\"harmony\" graphql:\"harmony\"" - Canto *float64 "json:\"canto\" graphql:\"canto\"" - Dogechain *float64 "json:\"dogechain\" graphql:\"dogechain\"" - Base *float64 "json:\"base\" graphql:\"base\"" - Blast *float64 "json:\"blast\" graphql:\"blast\"" - Scroll *float64 "json:\"scroll\" graphql:\"scroll\"" - Linea *float64 "json:\"linea\" graphql:\"linea\"" - Total *float64 "json:\"total\" graphql:\"total\"" + Date *string "json:\"date\" graphql:\"date\"" + Ethereum *float64 "json:\"ethereum\" graphql:\"ethereum\"" + Optimism *float64 "json:\"optimism\" graphql:\"optimism\"" + Cronos *float64 "json:\"cronos\" graphql:\"cronos\"" + Bsc *float64 "json:\"bsc\" graphql:\"bsc\"" + Polygon *float64 "json:\"polygon\" graphql:\"polygon\"" + Fantom *float64 "json:\"fantom\" graphql:\"fantom\"" + Boba *float64 "json:\"boba\" graphql:\"boba\"" + Metis *float64 "json:\"metis\" graphql:\"metis\"" + Moonbeam *float64 "json:\"moonbeam\" graphql:\"moonbeam\"" + Moonriver *float64 "json:\"moonriver\" graphql:\"moonriver\"" + Klaytn *float64 "json:\"klaytn\" graphql:\"klaytn\"" + Arbitrum *float64 "json:\"arbitrum\" graphql:\"arbitrum\"" + Avalanche *float64 "json:\"avalanche\" graphql:\"avalanche\"" + Dfk *float64 "json:\"dfk\" graphql:\"dfk\"" + Aurora *float64 "json:\"aurora\" graphql:\"aurora\"" + Harmony *float64 "json:\"harmony\" graphql:\"harmony\"" + Canto *float64 "json:\"canto\" graphql:\"canto\"" + Dogechain *float64 "json:\"dogechain\" graphql:\"dogechain\"" + Base *float64 "json:\"base\" graphql:\"base\"" + Blast *float64 "json:\"blast\" graphql:\"blast\"" + Scroll *float64 "json:\"scroll\" graphql:\"scroll\"" + Linea *float64 "json:\"linea\" graphql:\"linea\"" + Worldchain *float64 "json:\"worldchain\" graphql:\"worldchain\"" + Total *float64 "json:\"total\" graphql:\"total\"" }{ { Total: &valueFloat, diff --git a/services/explorer/consumer/parser/tokendata/cache.go b/services/explorer/consumer/parser/tokendata/cache.go index ab52591c94..b9eb33e54b 100644 --- a/services/explorer/consumer/parser/tokendata/cache.go +++ b/services/explorer/consumer/parser/tokendata/cache.go @@ -287,5 +287,10 @@ var tokenDataMap = map[string]TokenData{ "8453_0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb": {"DAI", 18, "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"}, "1_0xAdF7C35560035944e805D98fF17d58CDe2449389": {"SPEC", 18, "0xAdF7C35560035944e805D98fF17d58CDe2449389"}, "8453_0x96419929d7949D6A801A6909c145C8EEf6A40431": {"SPEC", 18, "0x96419929d7949D6A801A6909c145C8EEf6A40431"}, + "480_0x2cFc85d8E48F8EAB294be644d9E25C3030863003": {"WLD", 18, "0x2cFc85d8E48F8EAB294be644d9E25C3030863003"}, + "10_0xdC6fF44d5d932Cbd77B52E5612Ba0529DC6226F1": {"WLD", 18, "0xdC6fF44d5d932Cbd77B52E5612Ba0529DC6226F1"}, + "480_0x79A02482A880bCE3F13e09Da970dC34db4CD24d1": {"USDC.e", 6, "0x79A02482A880bCE3F13e09Da970dC34db4CD24d1"}, + "480_0x4200000000000000000000000000000000000006": {"WETH", 18, "0x4200000000000000000000000000000000000006"}, + "1_0x163f8c2467924be0ae7b5347228cabf260318753": {"WLD", 18, "0x163f8c2467924be0ae7b5347228cabf260318753"}, // Add additional tokens that are not part of the cache yet (and not by nature in bridge config) here } diff --git a/services/explorer/graphql/client/client.go b/services/explorer/graphql/client/client.go index 04f5498f6d..704c82de6b 100644 --- a/services/explorer/graphql/client/client.go +++ b/services/explorer/graphql/client/client.go @@ -106,30 +106,31 @@ type GetAmountStatistic struct { } type GetDailyStatisticsByChain struct { Response []*struct { - Date *string "json:\"date\" graphql:\"date\"" - Ethereum *float64 "json:\"ethereum\" graphql:\"ethereum\"" - Optimism *float64 "json:\"optimism\" graphql:\"optimism\"" - Cronos *float64 "json:\"cronos\" graphql:\"cronos\"" - Bsc *float64 "json:\"bsc\" graphql:\"bsc\"" - Polygon *float64 "json:\"polygon\" graphql:\"polygon\"" - Fantom *float64 "json:\"fantom\" graphql:\"fantom\"" - Boba *float64 "json:\"boba\" graphql:\"boba\"" - Metis *float64 "json:\"metis\" graphql:\"metis\"" - Moonbeam *float64 "json:\"moonbeam\" graphql:\"moonbeam\"" - Moonriver *float64 "json:\"moonriver\" graphql:\"moonriver\"" - Klaytn *float64 "json:\"klaytn\" graphql:\"klaytn\"" - Arbitrum *float64 "json:\"arbitrum\" graphql:\"arbitrum\"" - Avalanche *float64 "json:\"avalanche\" graphql:\"avalanche\"" - Dfk *float64 "json:\"dfk\" graphql:\"dfk\"" - Aurora *float64 "json:\"aurora\" graphql:\"aurora\"" - Harmony *float64 "json:\"harmony\" graphql:\"harmony\"" - Canto *float64 "json:\"canto\" graphql:\"canto\"" - Dogechain *float64 "json:\"dogechain\" graphql:\"dogechain\"" - Base *float64 "json:\"base\" graphql:\"base\"" - Blast *float64 "json:\"blast\" graphql:\"blast\"" - Scroll *float64 "json:\"scroll\" graphql:\"scroll\"" - Linea *float64 "json:\"linea\" graphql:\"linea\"" - Total *float64 "json:\"total\" graphql:\"total\"" + Date *string "json:\"date\" graphql:\"date\"" + Ethereum *float64 "json:\"ethereum\" graphql:\"ethereum\"" + Optimism *float64 "json:\"optimism\" graphql:\"optimism\"" + Cronos *float64 "json:\"cronos\" graphql:\"cronos\"" + Bsc *float64 "json:\"bsc\" graphql:\"bsc\"" + Polygon *float64 "json:\"polygon\" graphql:\"polygon\"" + Fantom *float64 "json:\"fantom\" graphql:\"fantom\"" + Boba *float64 "json:\"boba\" graphql:\"boba\"" + Metis *float64 "json:\"metis\" graphql:\"metis\"" + Moonbeam *float64 "json:\"moonbeam\" graphql:\"moonbeam\"" + Moonriver *float64 "json:\"moonriver\" graphql:\"moonriver\"" + Klaytn *float64 "json:\"klaytn\" graphql:\"klaytn\"" + Arbitrum *float64 "json:\"arbitrum\" graphql:\"arbitrum\"" + Avalanche *float64 "json:\"avalanche\" graphql:\"avalanche\"" + Dfk *float64 "json:\"dfk\" graphql:\"dfk\"" + Aurora *float64 "json:\"aurora\" graphql:\"aurora\"" + Harmony *float64 "json:\"harmony\" graphql:\"harmony\"" + Canto *float64 "json:\"canto\" graphql:\"canto\"" + Dogechain *float64 "json:\"dogechain\" graphql:\"dogechain\"" + Base *float64 "json:\"base\" graphql:\"base\"" + Blast *float64 "json:\"blast\" graphql:\"blast\"" + Scroll *float64 "json:\"scroll\" graphql:\"scroll\"" + Linea *float64 "json:\"linea\" graphql:\"linea\"" + Worldchain *float64 "json:\"worldchain\" graphql:\"worldchain\"" + Total *float64 "json:\"total\" graphql:\"total\"" } "json:\"response\" graphql:\"response\"" } type GetMessageBusTransactions struct { @@ -502,6 +503,7 @@ const GetDailyStatisticsByChainDocument = `query GetDailyStatisticsByChain ($cha blast scroll linea + worldchain total } } diff --git a/services/explorer/graphql/client/queries/queries.graphql b/services/explorer/graphql/client/queries/queries.graphql index 60eb3d63eb..3d351f3cc1 100644 --- a/services/explorer/graphql/client/queries/queries.graphql +++ b/services/explorer/graphql/client/queries/queries.graphql @@ -155,6 +155,7 @@ query GetDailyStatisticsByChain($chainID: Int, $type: DailyStatisticType, $durat blast scroll linea + worldchain total } } diff --git a/services/explorer/graphql/server/graph/model/models_gen.go b/services/explorer/graphql/server/graph/model/models_gen.go index d3eeb60af4..c090c167e4 100644 --- a/services/explorer/graphql/server/graph/model/models_gen.go +++ b/services/explorer/graphql/server/graph/model/models_gen.go @@ -81,30 +81,31 @@ type DateResult struct { // DateResult is a given statistic for a given date. type DateResultByChain struct { - Date *string `json:"date,omitempty"` - Ethereum *float64 `json:"ethereum,omitempty"` - Optimism *float64 `json:"optimism,omitempty"` - Cronos *float64 `json:"cronos,omitempty"` - Bsc *float64 `json:"bsc,omitempty"` - Polygon *float64 `json:"polygon,omitempty"` - Fantom *float64 `json:"fantom,omitempty"` - Boba *float64 `json:"boba,omitempty"` - Metis *float64 `json:"metis,omitempty"` - Moonbeam *float64 `json:"moonbeam,omitempty"` - Moonriver *float64 `json:"moonriver,omitempty"` - Klaytn *float64 `json:"klaytn,omitempty"` - Arbitrum *float64 `json:"arbitrum,omitempty"` - Avalanche *float64 `json:"avalanche,omitempty"` - Dfk *float64 `json:"dfk,omitempty"` - Aurora *float64 `json:"aurora,omitempty"` - Harmony *float64 `json:"harmony,omitempty"` - Canto *float64 `json:"canto,omitempty"` - Dogechain *float64 `json:"dogechain,omitempty"` - Base *float64 `json:"base,omitempty"` - Blast *float64 `json:"blast,omitempty"` - Scroll *float64 `json:"scroll,omitempty"` - Linea *float64 `json:"linea,omitempty"` - Total *float64 `json:"total,omitempty"` + Date *string `json:"date,omitempty"` + Ethereum *float64 `json:"ethereum,omitempty"` + Optimism *float64 `json:"optimism,omitempty"` + Cronos *float64 `json:"cronos,omitempty"` + Bsc *float64 `json:"bsc,omitempty"` + Polygon *float64 `json:"polygon,omitempty"` + Fantom *float64 `json:"fantom,omitempty"` + Boba *float64 `json:"boba,omitempty"` + Metis *float64 `json:"metis,omitempty"` + Moonbeam *float64 `json:"moonbeam,omitempty"` + Moonriver *float64 `json:"moonriver,omitempty"` + Klaytn *float64 `json:"klaytn,omitempty"` + Arbitrum *float64 `json:"arbitrum,omitempty"` + Avalanche *float64 `json:"avalanche,omitempty"` + Dfk *float64 `json:"dfk,omitempty"` + Aurora *float64 `json:"aurora,omitempty"` + Harmony *float64 `json:"harmony,omitempty"` + Canto *float64 `json:"canto,omitempty"` + Dogechain *float64 `json:"dogechain,omitempty"` + Base *float64 `json:"base,omitempty"` + Blast *float64 `json:"blast,omitempty"` + Scroll *float64 `json:"scroll,omitempty"` + Linea *float64 `json:"linea,omitempty"` + Worldchain *float64 `json:"worldchain,omitempty"` + Total *float64 `json:"total,omitempty"` } type HeroType struct { diff --git a/services/explorer/graphql/server/graph/partials.go b/services/explorer/graphql/server/graph/partials.go index 85519e442b..da436a4096 100644 --- a/services/explorer/graphql/server/graph/partials.go +++ b/services/explorer/graphql/server/graph/partials.go @@ -401,6 +401,7 @@ const dailyVolumeBridgeMvPt1 = ` results[81457] AS blast, results[534352] AS scroll, results[59144] AS linea, + results[480] AS worldchain, arraySum(mapValues(results)) AS total FROM (SELECT date, maxMap(map(chain_id, total)) AS results FROM (SELECT coalesce(toString(b.date), toString(s.date)) AS date, @@ -445,6 +446,7 @@ const dailyVolumeBridge = ` results[81457] AS blast, results[534352] AS scroll, results[59144] AS linea, + results[480] AS worldchain, arraySum(mapValues(results)) AS total FROM (SELECT date, maxMap(map(chain_id, total)) AS results FROM (SELECT coalesce(toString(b.date), toString(s.date)) AS date, @@ -541,6 +543,7 @@ SELECT date, results[81457] AS blast, results[534352] AS scroll, results[59144] AS linea, + results[480] AS worldchain, arraySum(mapValues(results)) AS total FROM (SELECT date, maxMap(map(chain_id, total)) AS results FROM (SELECT coalesce(toString(b.date), toString(s.date), toString(m.date)) AS date, @@ -643,6 +646,7 @@ SELECT date, results[81457] AS blast, results[534352] AS scroll, results[59144] AS linea, + results[480] AS worldchain, arraySum(mapValues(results)) AS total FROM ( SELECT date, @@ -674,6 +678,7 @@ SELECT date, results[81457] AS blast, results[534352] AS scroll, results[59144] AS linea, + results[480] AS worldchain, arraySum(mapValues(results)) AS total FROM ( SELECT date, @@ -706,6 +711,7 @@ SELECT date, results[81457] AS blast, results[534352] AS scroll, results[59144] AS linea, + results[480] AS worldchain, arraySum(mapValues(results)) AS total FROM ( SELECT date, diff --git a/services/explorer/graphql/server/graph/resolver/server.go b/services/explorer/graphql/server/graph/resolver/server.go index 8abe58001c..29689da5c4 100644 --- a/services/explorer/graphql/server/graph/resolver/server.go +++ b/services/explorer/graphql/server/graph/resolver/server.go @@ -100,30 +100,31 @@ type ComplexityRoot struct { } DateResultByChain struct { - Arbitrum func(childComplexity int) int - Aurora func(childComplexity int) int - Avalanche func(childComplexity int) int - Base func(childComplexity int) int - Blast func(childComplexity int) int - Boba func(childComplexity int) int - Bsc func(childComplexity int) int - Canto func(childComplexity int) int - Cronos func(childComplexity int) int - Date func(childComplexity int) int - Dfk func(childComplexity int) int - Dogechain func(childComplexity int) int - Ethereum func(childComplexity int) int - Fantom func(childComplexity int) int - Harmony func(childComplexity int) int - Klaytn func(childComplexity int) int - Linea func(childComplexity int) int - Metis func(childComplexity int) int - Moonbeam func(childComplexity int) int - Moonriver func(childComplexity int) int - Optimism func(childComplexity int) int - Polygon func(childComplexity int) int - Scroll func(childComplexity int) int - Total func(childComplexity int) int + Arbitrum func(childComplexity int) int + Aurora func(childComplexity int) int + Avalanche func(childComplexity int) int + Base func(childComplexity int) int + Blast func(childComplexity int) int + Boba func(childComplexity int) int + Bsc func(childComplexity int) int + Canto func(childComplexity int) int + Cronos func(childComplexity int) int + Date func(childComplexity int) int + Dfk func(childComplexity int) int + Dogechain func(childComplexity int) int + Ethereum func(childComplexity int) int + Fantom func(childComplexity int) int + Harmony func(childComplexity int) int + Klaytn func(childComplexity int) int + Linea func(childComplexity int) int + Metis func(childComplexity int) int + Moonbeam func(childComplexity int) int + Moonriver func(childComplexity int) int + Optimism func(childComplexity int) int + Polygon func(childComplexity int) int + Scroll func(childComplexity int) int + Total func(childComplexity int) int + Worldchain func(childComplexity int) int } HeroType struct { @@ -660,6 +661,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.DateResultByChain.Total(childComplexity), true + case "DateResultByChain.worldchain": + if e.complexity.DateResultByChain.Worldchain == nil { + break + } + + return e.complexity.DateResultByChain.Worldchain(childComplexity), true + case "HeroType.heroID": if e.complexity.HeroType.HeroID == nil { break @@ -1664,6 +1672,7 @@ type DateResultByChain { blast: Float scroll: Float linea: Float + worldchain: Float total: Float } @@ -4832,6 +4841,47 @@ func (ec *executionContext) fieldContext_DateResultByChain_linea(ctx context.Con return fc, nil } +func (ec *executionContext) _DateResultByChain_worldchain(ctx context.Context, field graphql.CollectedField, obj *model.DateResultByChain) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DateResultByChain_worldchain(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Worldchain, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*float64) + fc.Result = res + return ec.marshalOFloat2ᚖfloat64(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_DateResultByChain_worldchain(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "DateResultByChain", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Float does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _DateResultByChain_total(ctx context.Context, field graphql.CollectedField, obj *model.DateResultByChain) (ret graphql.Marshaler) { fc, err := ec.fieldContext_DateResultByChain_total(ctx, field) if err != nil { @@ -7190,6 +7240,8 @@ func (ec *executionContext) fieldContext_Query_dailyStatisticsByChain(ctx contex return ec.fieldContext_DateResultByChain_scroll(ctx, field) case "linea": return ec.fieldContext_DateResultByChain_linea(ctx, field) + case "worldchain": + return ec.fieldContext_DateResultByChain_worldchain(ctx, field) case "total": return ec.fieldContext_DateResultByChain_total(ctx, field) } @@ -10434,6 +10486,8 @@ func (ec *executionContext) _DateResultByChain(ctx context.Context, sel ast.Sele out.Values[i] = ec._DateResultByChain_scroll(ctx, field, obj) case "linea": out.Values[i] = ec._DateResultByChain_linea(ctx, field, obj) + case "worldchain": + out.Values[i] = ec._DateResultByChain_worldchain(ctx, field, obj) case "total": out.Values[i] = ec._DateResultByChain_total(ctx, field, obj) default: diff --git a/services/explorer/graphql/server/graph/schema/types.graphql b/services/explorer/graphql/server/graph/schema/types.graphql index 9b8bebdbd0..084c2da036 100644 --- a/services/explorer/graphql/server/graph/schema/types.graphql +++ b/services/explorer/graphql/server/graph/schema/types.graphql @@ -180,6 +180,7 @@ type DateResultByChain { blast: Float scroll: Float linea: Float + worldchain: Float total: Float } diff --git a/services/explorer/static/chainIDs.yaml b/services/explorer/static/chainIDs.yaml index 84add22b21..250c5db0a6 100644 --- a/services/explorer/static/chainIDs.yaml +++ b/services/explorer/static/chainIDs.yaml @@ -19,3 +19,4 @@ 81457: 'Blast' 534352: 'Scroll' 59144: 'Linea' +480: 'Worldchain' diff --git a/services/explorer/static/tokenIDToCoinGeckoID.yaml b/services/explorer/static/tokenIDToCoinGeckoID.yaml index 6c618da717..574cc7edc8 100644 --- a/services/explorer/static/tokenIDToCoinGeckoID.yaml +++ b/services/explorer/static/tokenIDToCoinGeckoID.yaml @@ -40,3 +40,4 @@ USDbC: 'usd-coin' crvUSD: 'usd-coin' USDB: 'usdb' SPEC: 'spectral' +WLD: 'worldcoin' diff --git a/services/explorer/static/tokenSymbolToCoinGeckoID.yaml b/services/explorer/static/tokenSymbolToCoinGeckoID.yaml index 231aa8ef74..6288259e8d 100644 --- a/services/explorer/static/tokenSymbolToCoinGeckoID.yaml +++ b/services/explorer/static/tokenSymbolToCoinGeckoID.yaml @@ -40,3 +40,4 @@ usdbc: 'usd-coin' crvusd: 'usd-coin' usdb: 'usdb' spec: 'spectral' +wld: 'worldcoin' diff --git a/services/explorer/static/tokenSymbolToTokenID.yaml b/services/explorer/static/tokenSymbolToTokenID.yaml index 83bac16402..eaf7f01c7d 100644 --- a/services/explorer/static/tokenSymbolToTokenID.yaml +++ b/services/explorer/static/tokenSymbolToTokenID.yaml @@ -42,3 +42,4 @@ usdbc: 'usd-coin' crvusd: 'usd-coin' usdb: 'usdb' spec: 'spectral' +wld: 'worldcoin'