/** * @license AngularJS v1.8.2 * (c) 2010-2020 Google LLC. http://angularjs.org * License: MIT */ (function(window, angular) {'use strict'; /* global shallowCopy: true */ /** * Creates a shallow copy of an object, an array or a primitive. * * Assumes that there are no proto properties for objects. */ function shallowCopy(src, dst) { if (isArray(src)) { dst = dst || []; for (var i = 0, ii = src.length; i < ii; i++) { dst[i] = src[i]; } } else if (isObject(src)) { dst = dst || {}; for (var key in src) { if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) { dst[key] = src[key]; } } } return dst || src; } /* global routeToRegExp: true */ /** * @param {string} path - The path to parse. (It is assumed to have query and hash stripped off.) * @param {Object} opts - Options. * @return {Object} - An object containing an array of path parameter names (`keys`) and a regular * expression (`regexp`) that can be used to identify a matching URL and extract the path * parameter values. * * @description * Parses the given path, extracting path parameter names and a regular expression to match URLs. * * Originally inspired by `pathRexp` in `visionmedia/express/lib/utils.js`. */ function routeToRegExp(path, opts) { var keys = []; var pattern = path .replace(/([().])/g, '\\$1') .replace(/(\/)?:(\w+)(\*\?|[?*])?/g, function(_, slash, key, option) { var optional = option === '?' || option === '*?'; var star = option === '*' || option === '*?'; keys.push({name: key, optional: optional}); slash = slash || ''; return ( (optional ? '(?:' + slash : slash + '(?:') + (star ? '(.+?)' : '([^/]+)') + (optional ? '?)?' : ')') ); }) .replace(/([/$*])/g, '\\$1'); if (opts.ignoreTrailingSlashes) { pattern = pattern.replace(/\/+$/, '') + '/*'; } return { keys: keys, regexp: new RegExp( '^' + pattern + '(?:[?#]|$)', opts.caseInsensitiveMatch ? 'i' : '' ) }; } /* global routeToRegExp: false */ /* global shallowCopy: false */ // `isArray` and `isObject` are necessary for `shallowCopy()` (included via `src/shallowCopy.js`). // They are initialized inside the `$RouteProvider`, to ensure `window.angular` is available. var isArray; var isObject; var isDefined; var noop; /** * @ngdoc module * @name ngRoute * @description * * The `ngRoute` module provides routing and deeplinking services and directives for AngularJS apps. * * ## Example * See {@link ngRoute.$route#examples $route} for an example of configuring and using `ngRoute`. * */ /* global -ngRouteModule */ var ngRouteModule = angular. module('ngRoute', []). info({ angularVersion: '1.8.2' }). provider('$route', $RouteProvider). // Ensure `$route` will be instantiated in time to capture the initial `$locationChangeSuccess` // event (unless explicitly disabled). This is necessary in case `ngView` is included in an // asynchronously loaded template. run(instantiateRoute); var $routeMinErr = angular.$$minErr('ngRoute'); var isEagerInstantiationEnabled; /** * @ngdoc provider * @name $routeProvider * @this * * @description * * Used for configuring routes. * * ## Example * See {@link ngRoute.$route#examples $route} for an example of configuring and using `ngRoute`. * * ## Dependencies * Requires the {@link ngRoute `ngRoute`} module to be installed. */ function $RouteProvider() { isArray = angular.isArray; isObject = angular.isObject; isDefined = angular.isDefined; noop = angular.noop; function inherit(parent, extra) { return angular.extend(Object.create(parent), extra); } var routes = {}; /** * @ngdoc method * @name $routeProvider#when * * @param {string} path Route path (matched against `$location.path`). If `$location.path` * contains redundant trailing slash or is missing one, the route will still match and the * `$location.path` will be updated to add or drop the trailing slash to exactly match the * route definition. * * * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up * to the next slash are matched and stored in `$routeParams` under the given `name` * when the route matches. * * `path` can contain named groups starting with a colon and ending with a star: * e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name` * when the route matches. * * `path` can contain optional named groups with a question mark: e.g.`:name?`. * * For example, routes like `/color/:color/largecode/:largecode*\/edit` will match * `/color/brown/largecode/code/with/slashes/edit` and extract: * * * `color: brown` * * `largecode: code/with/slashes`. * * * @param {Object} route Mapping information to be assigned to `$route.current` on route * match. * * Object properties: * * - `controller` – `{(string|Function)=}` – Controller fn that should be associated with * newly created scope or the name of a {@link angular.Module#controller registered * controller} if passed as a string. * - `controllerAs` – `{string=}` – An identifier name for a reference to the controller. * If present, the controller will be published to scope under the `controllerAs` name. * - `template` – `{(string|Function)=}` – html template as a string or a function that * returns an html template as a string which should be used by {@link * ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives. * This property takes precedence over `templateUrl`. * * If `template` is a function, it will be called with the following parameters: * * - `{Array.}` - route parameters extracted from the current * `$location.path()` by applying the current route * * One of `template` or `templateUrl` is required. * * - `templateUrl` – `{(string|Function)=}` – path or function that returns a path to an html * template that should be used by {@link ngRoute.directive:ngView ngView}. * * If `templateUrl` is a function, it will be called with the following parameters: * * - `{Array.}` - route parameters extracted from the current * `$location.path()` by applying the current route * * One of `templateUrl` or `template` is required. * * - `resolve` - `{Object.=}` - An optional map of dependencies which should * be injected into the controller. If any of these dependencies are promises, the router * will wait for them all to be resolved or one to be rejected before the controller is * instantiated. * If all the promises are resolved successfully, the values of the resolved promises are * injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is * fired. If any of the promises are rejected the * {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. * For easier access to the resolved dependencies from the template, the `resolve` map will * be available on the scope of the route, under `$resolve` (by default) or a custom name * specified by the `resolveAs` property (see below). This can be particularly useful, when * working with {@link angular.Module#component components} as route templates.
*
* **Note:** If your scope already contains a property with this name, it will be hidden * or overwritten. Make sure, you specify an appropriate name for this property, that * does not collide with other properties on the scope. *
* The map object is: * * - `key` – `{string}`: a name of a dependency to be injected into the controller. * - `factory` - `{string|Function}`: If `string` then it is an alias for a service. * Otherwise if function, then it is {@link auto.$injector#invoke injected} * and the return value is treated as the dependency. If the result is a promise, it is * resolved before its value is injected into the controller. Be aware that * `ngRoute.$routeParams` will still refer to the previous route within these resolve * functions. Use `$route.current.params` to access the new route parameters, instead. * * - `resolveAs` - `{string=}` - The name under which the `resolve` map will be available on * the scope of the route. If omitted, defaults to `$resolve`. * * - `redirectTo` – `{(string|Function)=}` – value to update * {@link ng.$location $location} path with and trigger route redirection. * * If `redirectTo` is a function, it will be called with the following parameters: * * - `{Object.}` - route parameters extracted from the current * `$location.path()` by applying the current route templateUrl. * - `{string}` - current `$location.path()` * - `{Object}` - current `$location.search()` * * The custom `redirectTo` function is expected to return a string which will be used * to update `$location.url()`. If the function throws an error, no further processing will * take place and the {@link ngRoute.$route#$routeChangeError $routeChangeError} event will * be fired. * * Routes that specify `redirectTo` will not have their controllers, template functions * or resolves called, the `$location` will be changed to the redirect url and route * processing will stop. The exception to this is if the `redirectTo` is a function that * returns `undefined`. In this case the route transition occurs as though there was no * redirection. * * - `resolveRedirectTo` – `{Function=}` – a function that will (eventually) return the value * to update {@link ng.$location $location} URL with and trigger route redirection. In * contrast to `redirectTo`, dependencies can be injected into `resolveRedirectTo` and the * return value can be either a string or a promise that will be resolved to a string. * * Similar to `redirectTo`, if the return value is `undefined` (or a promise that gets * resolved to `undefined`), no redirection takes place and the route transition occurs as * though there was no redirection. * * If the function throws an error or the returned promise gets rejected, no further * processing will take place and the * {@link ngRoute.$route#$routeChangeError $routeChangeError} event will be fired. * * `redirectTo` takes precedence over `resolveRedirectTo`, so specifying both on the same * route definition, will cause the latter to be ignored. * * - `[reloadOnUrl=true]` - `{boolean=}` - reload route when any part of the URL changes * (including the path) even if the new URL maps to the same route. * * If the option is set to `false` and the URL in the browser changes, but the new URL maps * to the same route, then a `$routeUpdate` event is broadcasted on the root scope (without * reloading the route). * * - `[reloadOnSearch=true]` - `{boolean=}` - reload route when only `$location.search()` * or `$location.hash()` changes. * * If the option is set to `false` and the URL in the browser changes, then a `$routeUpdate` * event is broadcasted on the root scope (without reloading the route). * *
* **Note:** This option has no effect if `reloadOnUrl` is set to `false`. *
* * - `[caseInsensitiveMatch=false]` - `{boolean=}` - match routes without being case sensitive * * If the option is set to `true`, then the particular route can be matched without being * case sensitive * * @returns {Object} self * * @description * Adds a new route definition to the `$route` service. */ this.when = function(path, route) { //copy original route object to preserve params inherited from proto chain var routeCopy = shallowCopy(route); if (angular.isUndefined(routeCopy.reloadOnUrl)) { routeCopy.reloadOnUrl = true; } if (angular.isUndefined(routeCopy.reloadOnSearch)) { routeCopy.reloadOnSearch = true; } if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) { routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch; } routes[path] = angular.extend( routeCopy, {originalPath: path}, path && routeToRegExp(path, routeCopy) ); // create redirection for trailing slashes if (path) { var redirectPath = (path[path.length - 1] === '/') ? path.substr(0, path.length - 1) : path + '/'; routes[redirectPath] = angular.extend( {originalPath: path, redirectTo: path}, routeToRegExp(redirectPath, routeCopy) ); } return this; }; /** * @ngdoc property * @name $routeProvider#caseInsensitiveMatch * @description * * A boolean property indicating if routes defined * using this provider should be matched using a case insensitive * algorithm. Defaults to `false`. */ this.caseInsensitiveMatch = false; /** * @ngdoc method * @name $routeProvider#otherwise * * @description * Sets route definition that will be used on route change when no other route definition * is matched. * * @param {Object|string} params Mapping information to be assigned to `$route.current`. * If called with a string, the value maps to `redirectTo`. * @returns {Object} self */ this.otherwise = function(params) { if (typeof params === 'string') { params = {redirectTo: params}; } this.when(null, params); return this; }; /** * @ngdoc method * @name $routeProvider#eagerInstantiationEnabled * @kind function * * @description * Call this method as a setter to enable/disable eager instantiation of the * {@link ngRoute.$route $route} service upon application bootstrap. You can also call it as a * getter (i.e. without any arguments) to get the current value of the * `eagerInstantiationEnabled` flag. * * Instantiating `$route` early is necessary for capturing the initial * {@link ng.$location#$locationChangeStart $locationChangeStart} event and navigating to the * appropriate route. Usually, `$route` is instantiated in time by the * {@link ngRoute.ngView ngView} directive. Yet, in cases where `ngView` is included in an * asynchronously loaded template (e.g. in another directive's template), the directive factory * might not be called soon enough for `$route` to be instantiated _before_ the initial * `$locationChangeSuccess` event is fired. Eager instantiation ensures that `$route` is always * instantiated in time, regardless of when `ngView` will be loaded. * * The default value is true. * * **Note**:
* You may want to disable the default behavior when unit-testing modules that depend on * `ngRoute`, in order to avoid an unexpected request for the default route's template. * * @param {boolean=} enabled - If provided, update the internal `eagerInstantiationEnabled` flag. * * @returns {*} The current value of the `eagerInstantiationEnabled` flag if used as a getter or * itself (for chaining) if used as a setter. */ isEagerInstantiationEnabled = true; this.eagerInstantiationEnabled = function eagerInstantiationEnabled(enabled) { if (isDefined(enabled)) { isEagerInstantiationEnabled = enabled; return this; } return isEagerInstantiationEnabled; }; this.$get = ['$rootScope', '$location', '$routeParams', '$q', '$injector', '$templateRequest', '$sce', '$browser', function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce, $browser) { /** * @ngdoc service * @name $route * @requires $location * @requires $routeParams * * @property {Object} current Reference to the current route definition. * The route definition contains: * * - `controller`: The controller constructor as defined in the route definition. * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for * controller instantiation. The `locals` contain * the resolved values of the `resolve` map. Additionally the `locals` also contain: * * - `$scope` - The current route scope. * - `$template` - The current route template HTML. * * The `locals` will be assigned to the route scope's `$resolve` property. You can override * the property name, using `resolveAs` in the route definition. See * {@link ngRoute.$routeProvider $routeProvider} for more info. * * @property {Object} routes Object with all route configuration Objects as its properties. * * @description * `$route` is used for deep-linking URLs to controllers and views (HTML partials). * It watches `$location.url()` and tries to map the path to an existing route definition. * * Requires the {@link ngRoute `ngRoute`} module to be installed. * * You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API. * * The `$route` service is typically used in conjunction with the * {@link ngRoute.directive:ngView `ngView`} directive and the * {@link ngRoute.$routeParams `$routeParams`} service. * * @example * This example shows how changing the URL hash causes the `$route` to match a route against the * URL, and the `ngView` pulls in the partial. * * * *
* Choose: * Moby | * Moby: Ch1 | * Gatsby | * Gatsby: Ch4 | * Scarlet Letter
* *
* *
* *
$location.path() = {{$location.path()}}
*
$route.current.templateUrl = {{$route.current.templateUrl}}
*
$route.current.params = {{$route.current.params}}
*
$route.current.scope.name = {{$route.current.scope.name}}
*
$routeParams = {{$routeParams}}
*
*
* * * controller: {{name}}
* Book Id: {{params.bookId}}
*
* * * controller: {{name}}
* Book Id: {{params.bookId}}
* Chapter Id: {{params.chapterId}} *
* * * angular.module('ngRouteExample', ['ngRoute']) * * .controller('MainController', function($scope, $route, $routeParams, $location) { * $scope.$route = $route; * $scope.$location = $location; * $scope.$routeParams = $routeParams; * }) * * .controller('BookController', function($scope, $routeParams) { * $scope.name = 'BookController'; * $scope.params = $routeParams; * }) * * .controller('ChapterController', function($scope, $routeParams) { * $scope.name = 'ChapterController'; * $scope.params = $routeParams; * }) * * .config(function($routeProvider, $locationProvider) { * $routeProvider * .when('/Book/:bookId', { * templateUrl: 'book.html', * controller: 'BookController', * resolve: { * // I will cause a 1 second delay * delay: function($q, $timeout) { * var delay = $q.defer(); * $timeout(delay.resolve, 1000); * return delay.promise; * } * } * }) * .when('/Book/:bookId/ch/:chapterId', { * templateUrl: 'chapter.html', * controller: 'ChapterController' * }); * * // configure html5 to get links working on jsfiddle * $locationProvider.html5Mode(true); * }); * * * * * it('should load and compile correct template', function() { * element(by.linkText('Moby: Ch1')).click(); * var content = element(by.css('[ng-view]')).getText(); * expect(content).toMatch(/controller: ChapterController/); * expect(content).toMatch(/Book Id: Moby/); * expect(content).toMatch(/Chapter Id: 1/); * * element(by.partialLinkText('Scarlet')).click(); * * content = element(by.css('[ng-view]')).getText(); * expect(content).toMatch(/controller: BookController/); * expect(content).toMatch(/Book Id: Scarlet/); * }); * *
*/ /** * @ngdoc event * @name $route#$routeChangeStart * @eventType broadcast on root scope * @description * Broadcasted before a route change. At this point the route services starts * resolving all of the dependencies needed for the route change to occur. * Typically this involves fetching the view template as well as any dependencies * defined in `resolve` route property. Once all of the dependencies are resolved * `$routeChangeSuccess` is fired. * * The route change (and the `$location` change that triggered it) can be prevented * by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} * for more details about event object. * * @param {Object} angularEvent Synthetic event object. * @param {Route} next Future route information. * @param {Route} current Current route information. */ /** * @ngdoc event * @name $route#$routeChangeSuccess * @eventType broadcast on root scope * @description * Broadcasted after a route change has happened successfully. * The `resolve` dependencies are now available in the `current.locals` property. * * {@link ngRoute.directive:ngView ngView} listens for the directive * to instantiate the controller and render the view. * * @param {Object} angularEvent Synthetic event object. * @param {Route} current Current route information. * @param {Route|Undefined} previous Previous route information, or undefined if current is * first route entered. */ /** * @ngdoc event * @name $route#$routeChangeError * @eventType broadcast on root scope * @description * Broadcasted if a redirection function fails or any redirection or resolve promises are * rejected. * * @param {Object} angularEvent Synthetic event object * @param {Route} current Current route information. * @param {Route} previous Previous route information. * @param {Route} rejection The thrown error or the rejection reason of the promise. Usually * the rejection reason is the error that caused the promise to get rejected. */ /** * @ngdoc event * @name $route#$routeUpdate * @eventType broadcast on root scope * @description * Broadcasted if the same instance of a route (including template, controller instance, * resolved dependencies, etc.) is being reused. This can happen if either `reloadOnSearch` or * `reloadOnUrl` has been set to `false`. * * @param {Object} angularEvent Synthetic event object * @param {Route} current Current/previous route information. */ var forceReload = false, preparedRoute, preparedRouteIsUpdateOnly, $route = { routes: routes, /** * @ngdoc method * @name $route#reload * * @description * Causes `$route` service to reload the current route even if * {@link ng.$location $location} hasn't changed. * * As a result of that, {@link ngRoute.directive:ngView ngView} * creates new scope and reinstantiates the controller. */ reload: function() { forceReload = true; var fakeLocationEvent = { defaultPrevented: false, preventDefault: function fakePreventDefault() { this.defaultPrevented = true; forceReload = false; } }; $rootScope.$evalAsync(function() { prepareRoute(fakeLocationEvent); if (!fakeLocationEvent.defaultPrevented) commitRoute(); }); }, /** * @ngdoc method * @name $route#updateParams * * @description * Causes `$route` service to update the current URL, replacing * current route parameters with those specified in `newParams`. * Provided property names that match the route's path segment * definitions will be interpolated into the location's path, while * remaining properties will be treated as query params. * * @param {!Object} newParams mapping of URL parameter names to values */ updateParams: function(newParams) { if (this.current && this.current.$$route) { newParams = angular.extend({}, this.current.params, newParams); $location.path(interpolate(this.current.$$route.originalPath, newParams)); // interpolate modifies newParams, only query params are left $location.search(newParams); } else { throw $routeMinErr('norout', 'Tried updating route with no current route'); } } }; $rootScope.$on('$locationChangeStart', prepareRoute); $rootScope.$on('$locationChangeSuccess', commitRoute); return $route; ///////////////////////////////////////////////////// /** * @param on {string} current url * @param route {Object} route regexp to match the url against * @return {?Object} * * @description * Check if the route matches the current url. * * Inspired by match in * visionmedia/express/lib/router/router.js. */ function switchRouteMatcher(on, route) { var keys = route.keys, params = {}; if (!route.regexp) return null; var m = route.regexp.exec(on); if (!m) return null; for (var i = 1, len = m.length; i < len; ++i) { var key = keys[i - 1]; var val = m[i]; if (key && val) { params[key.name] = val; } } return params; } function prepareRoute($locationEvent) { var lastRoute = $route.current; preparedRoute = parseRoute(); preparedRouteIsUpdateOnly = isNavigationUpdateOnly(preparedRoute, lastRoute); if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) { if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) { if ($locationEvent) { $locationEvent.preventDefault(); } } } } function commitRoute() { var lastRoute = $route.current; var nextRoute = preparedRoute; if (preparedRouteIsUpdateOnly) { lastRoute.params = nextRoute.params; angular.copy(lastRoute.params, $routeParams); $rootScope.$broadcast('$routeUpdate', lastRoute); } else if (nextRoute || lastRoute) { forceReload = false; $route.current = nextRoute; var nextRoutePromise = $q.resolve(nextRoute); $browser.$$incOutstandingRequestCount('$route'); nextRoutePromise. then(getRedirectionData). then(handlePossibleRedirection). then(function(keepProcessingRoute) { return keepProcessingRoute && nextRoutePromise. then(resolveLocals). then(function(locals) { // after route change if (nextRoute === $route.current) { if (nextRoute) { nextRoute.locals = locals; angular.copy(nextRoute.params, $routeParams); } $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute); } }); }).catch(function(error) { if (nextRoute === $route.current) { $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error); } }).finally(function() { // Because `commitRoute()` is called from a `$rootScope.$evalAsync` block (see // `$locationWatch`), this `$$completeOutstandingRequest()` call will not cause // `outstandingRequestCount` to hit zero. This is important in case we are redirecting // to a new route which also requires some asynchronous work. $browser.$$completeOutstandingRequest(noop, '$route'); }); } } function getRedirectionData(route) { var data = { route: route, hasRedirection: false }; if (route) { if (route.redirectTo) { if (angular.isString(route.redirectTo)) { data.path = interpolate(route.redirectTo, route.params); data.search = route.params; data.hasRedirection = true; } else { var oldPath = $location.path(); var oldSearch = $location.search(); var newUrl = route.redirectTo(route.pathParams, oldPath, oldSearch); if (angular.isDefined(newUrl)) { data.url = newUrl; data.hasRedirection = true; } } } else if (route.resolveRedirectTo) { return $q. resolve($injector.invoke(route.resolveRedirectTo)). then(function(newUrl) { if (angular.isDefined(newUrl)) { data.url = newUrl; data.hasRedirection = true; } return data; }); } } return data; } function handlePossibleRedirection(data) { var keepProcessingRoute = true; if (data.route !== $route.current) { keepProcessingRoute = false; } else if (data.hasRedirection) { var oldUrl = $location.url(); var newUrl = data.url; if (newUrl) { $location. url(newUrl). replace(); } else { newUrl = $location. path(data.path). search(data.search). replace(). url(); } if (newUrl !== oldUrl) { // Exit out and don't process current next value, // wait for next location change from redirect keepProcessingRoute = false; } } return keepProcessingRoute; } function resolveLocals(route) { if (route) { var locals = angular.extend({}, route.resolve); angular.forEach(locals, function(value, key) { locals[key] = angular.isString(value) ? $injector.get(value) : $injector.invoke(value, null, null, key); }); var template = getTemplateFor(route); if (angular.isDefined(template)) { locals['$template'] = template; } return $q.all(locals); } } function getTemplateFor(route) { var template, templateUrl; if (angular.isDefined(template = route.template)) { if (angular.isFunction(template)) { template = template(route.params); } } else if (angular.isDefined(templateUrl = route.templateUrl)) { if (angular.isFunction(templateUrl)) { templateUrl = templateUrl(route.params); } if (angular.isDefined(templateUrl)) { route.loadedTemplateUrl = $sce.valueOf(templateUrl); template = $templateRequest(templateUrl); } } return template; } /** * @returns {Object} the current active route, by matching it against the URL */ function parseRoute() { // Match a route var params, match; angular.forEach(routes, function(route, path) { if (!match && (params = switchRouteMatcher($location.path(), route))) { match = inherit(route, { params: angular.extend({}, $location.search(), params), pathParams: params}); match.$$route = route; } }); // No route matched; fallback to "otherwise" route return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}}); } /** * @param {Object} newRoute - The new route configuration (as returned by `parseRoute()`). * @param {Object} oldRoute - The previous route configuration (as returned by `parseRoute()`). * @returns {boolean} Whether this is an "update-only" navigation, i.e. the URL maps to the same * route and it can be reused (based on the config and the type of change). */ function isNavigationUpdateOnly(newRoute, oldRoute) { // IF this is not a forced reload return !forceReload // AND both `newRoute`/`oldRoute` are defined && newRoute && oldRoute // AND they map to the same Route Definition Object && (newRoute.$$route === oldRoute.$$route) // AND `reloadOnUrl` is disabled && (!newRoute.reloadOnUrl // OR `reloadOnSearch` is disabled || (!newRoute.reloadOnSearch // AND both routes have the same path params && angular.equals(newRoute.pathParams, oldRoute.pathParams) ) ); } /** * @returns {string} interpolation of the redirect path with the parameters */ function interpolate(string, params) { var result = []; angular.forEach((string || '').split(':'), function(segment, i) { if (i === 0) { result.push(segment); } else { var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/); var key = segmentMatch[1]; result.push(params[key]); result.push(segmentMatch[2] || ''); delete params[key]; } }); return result.join(''); } }]; } instantiateRoute.$inject = ['$injector']; function instantiateRoute($injector) { if (isEagerInstantiationEnabled) { // Instantiate `$route` $injector.get('$route'); } } ngRouteModule.provider('$routeParams', $RouteParamsProvider); /** * @ngdoc service * @name $routeParams * @requires $route * @this * * @description * The `$routeParams` service allows you to retrieve the current set of route parameters. * * Requires the {@link ngRoute `ngRoute`} module to be installed. * * The route parameters are a combination of {@link ng.$location `$location`}'s * {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}. * The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched. * * In case of parameter name collision, `path` params take precedence over `search` params. * * The service guarantees that the identity of the `$routeParams` object will remain unchanged * (but its properties will likely change) even when a route change occurs. * * Note that the `$routeParams` are only updated *after* a route change completes successfully. * This means that you cannot rely on `$routeParams` being correct in route resolve functions. * Instead you can use `$route.current.params` to access the new route's parameters. * * @example * ```js * // Given: * // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby * // Route: /Chapter/:chapterId/Section/:sectionId * // * // Then * $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'} * ``` */ function $RouteParamsProvider() { this.$get = function() { return {}; }; } ngRouteModule.directive('ngView', ngViewFactory); ngRouteModule.directive('ngView', ngViewFillContentFactory); /** * @ngdoc directive * @name ngView * @restrict ECA * * @description * `ngView` is a directive that complements the {@link ngRoute.$route $route} service by * including the rendered template of the current route into the main layout (`index.html`) file. * Every time the current route changes, the included view changes with it according to the * configuration of the `$route` service. * * Requires the {@link ngRoute `ngRoute`} module to be installed. * * @animations * | Animation | Occurs | * |----------------------------------|-------------------------------------| * | {@link ng.$animate#enter enter} | when the new element is inserted to the DOM | * | {@link ng.$animate#leave leave} | when the old element is removed from to the DOM | * * The enter and leave animation occur concurrently. * * @scope * @priority 400 * @param {string=} onload Expression to evaluate whenever the view updates. * * @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll * $anchorScroll} to scroll the viewport after the view is updated. * * - If the attribute is not set, disable scrolling. * - If the attribute is set without value, enable scrolling. * - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated * as an expression yields a truthy value. * @example
Choose: Moby | Moby: Ch1 | Gatsby | Gatsby: Ch4 | Scarlet Letter

$location.path() = {{main.$location.path()}}
$route.current.templateUrl = {{main.$route.current.templateUrl}}
$route.current.params = {{main.$route.current.params}}
$routeParams = {{main.$routeParams}}
controller: {{book.name}}
Book Id: {{book.params.bookId}}
controller: {{chapter.name}}
Book Id: {{chapter.params.bookId}}
Chapter Id: {{chapter.params.chapterId}}
.view-animate-container { position:relative; height:100px!important; background:white; border:1px solid black; height:40px; overflow:hidden; } .view-animate { padding:10px; } .view-animate.ng-enter, .view-animate.ng-leave { transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; display:block; width:100%; border-left:1px solid black; position:absolute; top:0; left:0; right:0; bottom:0; padding:10px; } .view-animate.ng-enter { left:100%; } .view-animate.ng-enter.ng-enter-active { left:0; } .view-animate.ng-leave.ng-leave-active { left:-100%; } angular.module('ngViewExample', ['ngRoute', 'ngAnimate']) .config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { $routeProvider .when('/Book/:bookId', { templateUrl: 'book.html', controller: 'BookCtrl', controllerAs: 'book' }) .when('/Book/:bookId/ch/:chapterId', { templateUrl: 'chapter.html', controller: 'ChapterCtrl', controllerAs: 'chapter' }); $locationProvider.html5Mode(true); }]) .controller('MainCtrl', ['$route', '$routeParams', '$location', function MainCtrl($route, $routeParams, $location) { this.$route = $route; this.$location = $location; this.$routeParams = $routeParams; }]) .controller('BookCtrl', ['$routeParams', function BookCtrl($routeParams) { this.name = 'BookCtrl'; this.params = $routeParams; }]) .controller('ChapterCtrl', ['$routeParams', function ChapterCtrl($routeParams) { this.name = 'ChapterCtrl'; this.params = $routeParams; }]); it('should load and compile correct template', function() { element(by.linkText('Moby: Ch1')).click(); var content = element(by.css('[ng-view]')).getText(); expect(content).toMatch(/controller: ChapterCtrl/); expect(content).toMatch(/Book Id: Moby/); expect(content).toMatch(/Chapter Id: 1/); element(by.partialLinkText('Scarlet')).click(); content = element(by.css('[ng-view]')).getText(); expect(content).toMatch(/controller: BookCtrl/); expect(content).toMatch(/Book Id: Scarlet/); });
*/ /** * @ngdoc event * @name ngView#$viewContentLoaded * @eventType emit on the current ngView scope * @description * Emitted every time the ngView content is reloaded. */ ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate']; function ngViewFactory($route, $anchorScroll, $animate) { return { restrict: 'ECA', terminal: true, priority: 400, transclude: 'element', link: function(scope, $element, attr, ctrl, $transclude) { var currentScope, currentElement, previousLeaveAnimation, autoScrollExp = attr.autoscroll, onloadExp = attr.onload || ''; scope.$on('$routeChangeSuccess', update); update(); function cleanupLastView() { if (previousLeaveAnimation) { $animate.cancel(previousLeaveAnimation); previousLeaveAnimation = null; } if (currentScope) { currentScope.$destroy(); currentScope = null; } if (currentElement) { previousLeaveAnimation = $animate.leave(currentElement); previousLeaveAnimation.done(function(response) { if (response !== false) previousLeaveAnimation = null; }); currentElement = null; } } function update() { var locals = $route.current && $route.current.locals, template = locals && locals.$template; if (angular.isDefined(template)) { var newScope = scope.$new(); var current = $route.current; // Note: This will also link all children of ng-view that were contained in the original // html. If that content contains controllers, ... they could pollute/change the scope. // However, using ng-view on an element with additional content does not make sense... // Note: We can't remove them in the cloneAttchFn of $transclude as that // function is called before linking the content, which would apply child // directives to non existing elements. var clone = $transclude(newScope, function(clone) { $animate.enter(clone, null, currentElement || $element).done(function onNgViewEnter(response) { if (response !== false && angular.isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { $anchorScroll(); } }); cleanupLastView(); }); currentElement = clone; currentScope = current.scope = newScope; currentScope.$emit('$viewContentLoaded'); currentScope.$eval(onloadExp); } else { cleanupLastView(); } } } }; } // This directive is called during the $transclude call of the first `ngView` directive. // It will replace and compile the content of the element with the loaded template. // We need this directive so that the element content is already filled when // the link function of another directive on the same element as ngView // is called. ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route']; function ngViewFillContentFactory($compile, $controller, $route) { return { restrict: 'ECA', priority: -400, link: function(scope, $element) { var current = $route.current, locals = current.locals; $element.html(locals.$template); var link = $compile($element.contents()); if (current.controller) { locals.$scope = scope; var controller = $controller(current.controller, locals); if (current.controllerAs) { scope[current.controllerAs] = controller; } $element.data('$ngControllerController', controller); $element.children().data('$ngControllerController', controller); } scope[current.resolveAs || '$resolve'] = locals; link(scope); } }; } })(window, window.angular);
Jackpot Games
Bang Gacor 1000
Bang Gacor 1000
NEKO X NEKO
NEKO X NEKO
Tokyo Drift Nitro
Tokyo Drift Nitro
Mines+
Mines+
Koi Gate
Koi Gate
The Crypt
The Crypt
New Games
Fortune of Olympus
Fortune of Olympus
Roma Legacy
Roma Legacy
Hot Hot Fruit
Hot Hot Fruit
Wild Bandito
Wild Bandito
Wanted Dead Or Wild
Wanted Dead Or Wild
Wild Beans
Wild Beans
Game Populer
Mahjong Wins 2
Mahjong Wins 2
Fortune Monkey
Fortune Monkey
The Great Icescape
The Great Icescape
Wukong Black Scatter
Wukong Black Scatter
Crypto Mania
Crypto Mania
Trump Card
Trump Card

Casinos That Accept Ethereum UK 2026: Top Ethereum Casino Sites Compared

The UK Gambling Commission has not licensed a single casino to accept Ethereum as a deposit method in 2026. That is not an oversight. It is policy. UK-licensed operators stick to debit cards, bank transfers, and the occasional e-wallet, but crypto is off the table. So if you want to play slots or blackjack with ETH from a UK IP address, you are looking at offshore casino sites. Some of them are reputable. Some are not. This guide separates the ones worth your time from the ones that will make you wish you had just burned the ETH instead.

We compared more than 20 Ethereum-friendly platforms, checked their game libraries, withdrawal behaviour, and bonus terms. The result is a ranked table plus the details that matter if you actually want your money back after a win.

Before you scroll, one thing: none of these sites hold a UKGC licence. That means no UK ombudsman, no GamStop integration, and no UK consumer protection if the operator disappears. Use the comparison as a starting point, not a guarantee.

How We Selected These Ethereum Casinos

We started with a simple question: would we deposit our own ETH here? Then we applied five filters.

  • Licence and ownership transparency — a valid Curacao or equivalent licence, plus a named operator that is not hiding behind a shell company.
  • Game variety from known studios — if a casino only offers unbranded slots with no provider names, it is likely running pirated software.
  • Withdrawal speed and limits — an Ethereum casino that takes three days to process a withdrawal is missing the point of Ethereum.
  • Bonus terms that are not hostile — wagering requirements above 40x are a red flag, and so is a maximum bet rule of 0.50 mBTC when you are playing with ETH.
  • Customer support that answers in English — live chat or email, but a human, not a bot that loops through the same three replies.

We did not weight ‘biggest welcome bonus’ heavily. A 5 BTC bonus with a 60x rollover is just a marketing trick to keep you depositing.

Top Ethereum Casinos Accepting UK Players in 2026

The table below lists ten platforms that consistently accept UK registrations and support Ethereum deposits and withdrawals. Availability changes without notice, so always check the operator’s terms and the cashier before signing up.

Casino Bonus Type Licence Game Selection Withdrawal Speed ETH Min Deposit
Cloudbet Welcome bonus + free spins Curacao Slots, live casino, sports Under 1 hour Low
BitStarz Welcome package Curacao 3000+ slots, table games Under 30 min Low
mBit Welcome bonus + cashback Curacao Slots, live dealer Under 1 hour Low
7Bit Welcome package + free spins Curacao Slots, jackpots Under 1 hour Low
FortuneJack Welcome bonus + cashback Curacao Slots, dice, live Under 1 hour Low
Metaspins Welcome bonus + rakeback Curacao Slots, live casino Instant after confirm Low
Wild.io Welcome package Curacao Slots, table games Under 1 hour Low
Bets.io Welcome bonus + free spins Curacao Slots, live dealer Under 1 hour Low
Jackbit Welcome bonus + free spins Curacao Slots, mini games Under 1 hour Low
Vave Welcome bonus + cashback Curacao Slots, sports Under 1 hour Low

The ‘low’ minimum deposit is relative to Ethereum’s price. Typically, these casinos accept deposits starting around 0.001 ETH, but the cashier will show the exact amount in ETH at the time of deposit.

Cloudbet

Cloudbet has been around since 2013, which makes it ancient in crypto casino years. It runs on a Curacao licence and offers a sportsbook alongside the casino. The Ethereum experience is smooth: deposits show up after a few confirmations, and withdrawals are processed quickly, usually within an hour. The welcome bonus is generic, but the ongoing cashback and reload offers are where the value sits if you play regularly.

One detail that matters: Cloudbet converts your ETH into a fiat-pegged balance by default. That means your bankroll does not swing with the Ethereum price while you are mid-session. For players who want pure ETH exposure, there is an option to keep the balance in crypto, but you have to enable it in the settings.

BitStarz

BitStarz is the loudest name in Ethereum gambling, and for once the noise is mostly justified. The game library exceeds 3,000 titles from companies like NetEnt, Pragmatic Play, and Evolution. The platform accepts Ethereum directly, but it also converts to EUR or USD if you prefer a stable balance. Withdrawal times are genuinely fast—often under 30 minutes after the transaction is approved.

The wagering requirement on the first deposit bonus can feel high if you are not used to crypto casino terms. The reload bonuses are more reasonable, and the cashback offers are straightforward. BitStarz also runs regular slot tournaments with prize pools in crypto, which is a decent way to stretch a small bankroll if you are disciplined.

mBit

mBit targets Ethereum and Bitcoin players with a clean interface and a decent live dealer section. The welcome package includes free spins, but the real draw is the daily cashback and the VIP ladder that is slightly less insulting than the industry average. Withdrawals are quick, but identity verification can kick in if you deposit large amounts, so keep a utility bill handy.

mBit’s loyalty programme works on a points system that converts to cashback. It is not life-changing, but it is predictable. If you plan to play regularly, the cashback reduces the effective house edge a little, which is more than most casinos offer.

7Bit

7Bit has a retro vibe and a huge slot collection, including many jackpot titles. Ethereum deposits are accepted without drama. The welcome package often includes free spins, but read the terms: those spins are usually tied to specific slots and expire within a few days.

Withdrawal times hover around an hour for Ethereum, which is acceptable. The site runs frequent promotions, but the wagering requirements on those promos are often higher than the standard welcome offer. Stick to cashback and reload bonuses if you can.

FortuneJack

FortuneJack is one of the oldest crypto casinos and still worth a look. It offers dice games and provably fair titles alongside standard slots. The bonus structure is more cashback-heavy than free-spin-heavy, which suits conservative players. Ethereum withdrawals are processed quickly, and the support team is responsive in English.

FortuneJack’s dice game is a nice break from slots because the house edge is transparent and you can adjust the odds. It is not a reason to deposit more, but it is a reason to keep the site on your list if you like low-stakes gambling.

Casinos That Accept Ewallets UK 2026: Top Sites, Deposits, Withdrawals

Metaspins, Wild.io, Bets.io, Jackbit, and Vave round out the table. They are newer and smaller, but each has a legitimate game library and functional Ethereum cashier. Metaspins is notable for instant withdrawals and a rakeback system, while Vave leans into sports betting alongside casino games. Jackbit is a good choice if you want a no-frills slots experience with fast payouts.

The UK Legal Position for Ethereum Casinos

Ethereum gambling in the UK sits in a strange legal fog. The Gambling Act 2005 regulates gambling operators that target British consumers, but the UKGC has not issued a licence to any casino for crypto payments. That means any site accepting Ethereum and UK players is operating outside UK regulation.

Is that illegal? Not for the player in most cases. UK law does not criminalise placing a bet at an offshore casino. But it does remove the protections you would normally have: no mandatory affordability checks, no formal complaints process, and no guarantee that the operator will follow its own terms.

The practical risk is counterparty risk. If the casino decides not to pay a large win, you have limited recourse. Some operators use third-party dispute services, but they have no legal teeth. Choose platforms with long operating histories and public leadership.

One more point: UK payment providers sometimes block transactions to known gambling sites, even if the site is legal. That mostly affects card payments, not Ethereum transfers. But if your bank flags a deposit to a crypto exchange, that is a separate issue. Using Ethereum to gamble does not make the gambling itself illegal, but it does make it harder for banks to see what you are doing, which some players consider a feature.

Casinos That Accept Litecoin UK 2026: The Truth Behind the Search

What a Curacao Licence Really Means for UK Players

Most Ethereum casinos on this list hold a Curacao licence. That sounds official, but Curacao has historically been a light-touch regulator. The jurisdiction introduced the Curacao Gaming Control Board in 2024, which tightened some rules, but enforcement remains weaker than the UKGC or MGA.

A Curacao licence does not mean the casino is cheating you. It means the operator passed a basic due diligence check and pays an annual fee. Dispute resolution is slow and often favours the operator. For UK players, that is the trade-off: you get Ethereum payments and fewer ID checks, but you lose the consumer protections of a UK licence.

Some casinos also hold licences from other jurisdictions, but those are less common for crypto-focused sites. Always verify the licence number on the regulator’s website before depositing.

How Ethereum Casinos Work: A Technical Primer

Ethereum casinos use the same backend as fiat casinos, but the payment layer is different. You send ETH from your wallet to a unique deposit address. The casino’s system watches the blockchain for confirmations and credits your account after a set number.

After that, your balance may be stored in ETH, a stablecoin, or fiat. Withdrawals reverse the process: the casino sends ETH from its hot wallet to your address. The transaction is visible on Etherscan, which adds a layer of transparency that fiat casinos cannot match.

Some casinos use smart contracts for provably fair games. A smart contract can hold your bet and release winnings automatically based on a random number generated on-chain. That sounds impressive, but in practice most casinos still use traditional RNG software. The smart contract games are a niche.

Ethereum Deposits and Withdrawals: What Actually Happens

When you deposit Ethereum at an online casino, the site gives you a deposit address. You send ETH from your wallet. The casino waits for a certain number of network confirmations—often between one and six—then credits your account. The whole process usually takes under ten minutes, but it can stretch if network congestion is high and gas fees spike.

Some casinos convert your ETH into a stablecoin or fiat balance immediately. That protects you from price swings while you play. Others keep your balance in ETH, so if the price drops 10% overnight, your bankroll does too. Always check which model the casino uses before you deposit.

Withdrawals are where Ethereum shines. Instead of waiting three to five business days for a bank transfer, you can receive ETH back to your wallet in under an hour. The casino may deduct a network fee, typically a small amount in ETH, but compared with e-wallet fees, it is negligible. Watch for minimum withdrawal limits and any manual approval steps that can add delays.

Gas fees on the Ethereum mainnet have dropped since the 2024 upgrades, but they still spike during network congestion. Some casinos now support layer-2 networks like Arbitrum or Optimism for Ethereum deposits, which cuts fees to a few cents. Not every operator advertises this, so ask support if you want to avoid mainnet costs.

Ethereum Casino Bonuses: The Fine Print You Can’t Ignore

A casino ‘gift’ is as free as a hotel minibar. The welcome bonus might say 100% up to 1 ETH, but the wagering requirement tells the real story. If you have to wager the bonus 40 times before withdrawing, the expected value is negative—always. That is not a bug; it is the business model.

Let’s do the maths. You deposit 0.1 ETH and get a 100% match, so you have 0.2 ETH to play with. The wagering requirement is 40x the bonus plus deposit, which means you must place 8 ETH worth of bets before you can withdraw. On a slot with a 96% RTP, you would expect to lose 0.32 ETH over those 8 ETH in wagers. That is more than your entire starting balance. The bonus is not a gift; it is a retention mechanism disguised as generosity.

Free spins are the same trick with a prettier wrapper. A batch of 50 free spins on a slot with a 35x wagering requirement on winnings rarely leaves you with anything. And if the free spins are tied to a low-RTP slot, the house edge eats the ‘free’ value before you even click spin.

No deposit bonuses are practically extinct at reputable Ethereum casinos. When they do appear, they come with max cashout limits of a few pounds and wagering requirements that make the offer pointless. Cashback and rakeback are more honest: you get a percentage of losses or wagered amount back, usually without a wagering requirement. That is the only bonus worth chasing.

Games You Can Play with Ethereum

The game library at an Ethereum casino is usually identical to a fiat online casino. You get slots from Pragmatic Play, NetEnt, Play’n GO, and others. Live dealer tables from Evolution and Pragmatic Live are common, so you can play blackjack, roulette, and baccarat with a real croupier and an Ethereum balance.

Where Ethereum casinos differ is in provably fair games. Dice, crash games, and some table games use cryptographic algorithms to prove that outcomes were not tampered with. You can verify each bet’s fairness using the hash provided. It is a nice feature, but in practice most players stick to traditional slots and live games where the house edge is well understood.

Slots dominate the lobby, and that is by design. The house edge on slots ranges from 2% to 6%, which is far worse than blackjack or roulette. If you are playing with Ethereum, the volatility is compounded by the coin’s price movement. A session that looks break-even in ETH terms can be a loss in GBP terms if the value drops overnight.

Mobile and App Experience for Ethereum Casinos

Almost every Ethereum casino works in a mobile browser, and most are responsive enough to play slots without pinching and zooming. Some operators, like BitStarz and Cloudbet, offer dedicated Android apps or progressive web apps that you can install from the browser. The iOS App Store is still hostile to real-money gambling apps, so iPhone users will rely on the browser.

If you use MetaMask, some casinos allow you to connect directly and sign transactions from the dApp interface. That is convenient, but it also means your wallet is exposed to the casino’s smart contract. For casual play, a regular wallet and deposit address is safer.

Performance on mobile is generally good, but live dealer games can stutter if your connection is weak. Use a stable Wi-Fi connection for live blackjack, because a freeze mid-hand costs real money.

New Ethereum Casinos to Watch in 2026

The 2026 cohort of Ethereum casinos is focused on lower fees, faster withdrawals, and better mobile UX. Names like Jackbit and Vave are still building their reputations, but they have solid game libraries and no withdrawal horror stories so far. The trend is toward no-KYC platforms for small deposits, but that also means less protection if something goes wrong. Keep deposits small at any new brand until it proves it can pay.

Another trend is the integration of Ethereum layer-2 networks to reduce transaction costs. A few operators now accept ETH on Arbitrum or Optimism, which makes micro-deposits practical. This is a genuine improvement, but it also fragments liquidity and can confuse players who are not familiar with L2 bridges. For most UK players, mainnet ETH remains the default.

Responsible Gambling with Crypto

Ethereum’s volatility adds a second layer of risk to gambling. You can lose money on the game and lose money on the exchange rate at the same time. Set a budget in GBP, not ETH, and convert that limit into ETH at the moment of deposit. Do not chase losses by depositing more ETH when the price dips—that is a double loss.

Offshore casinos do not integrate with GamStop, so self-exclusion is not automatic. You must use the operator’s own cool-off or self-exclusion tools, and they are only as good as the operator’s willingness to enforce them. Consider a separate wallet for gambling and keep only the amount you can afford to lose in it.

Casinos That Accept Interac UK 2026: Complete Guide to Deposits, Withdrawals, and Safer Alternatives

If you need support, organisations like GamCare and BeGambleAware operate in the UK and offer free advice. They cannot block offshore casinos, but they can help you stop.

Common Ethereum Casino Scams and How to Avoid Them

Fake licences are the oldest trick in the book. A site claims to be licensed by Curacao, but the licence number does not check out on the regulator’s website. Always verify the licence before depositing.

Slow or refused withdrawals are the most common complaint. A casino will happily accept your ETH, but when you try to withdraw a big win, suddenly you need to provide documents, or the withdrawal is ‘under review’ for weeks. Legitimate casinos process withdrawals in under 24 hours. If a site stalls, stop playing and contact support immediately.

Phishing is another risk. Fake casino sites impersonate real brands and steal your deposits. Always type the casino URL directly into the browser, never click links from unsolicited emails or Telegram groups.

Ethereum vs Other Crypto at Online Casinos

Bitcoin remains the default crypto at most casinos, but Ethereum has advantages. ETH transactions are typically faster than Bitcoin’s 10-minute block time, and gas fees are often lower during normal network conditions. Ethereum also supports smart contracts, which some casinos use for provably fair games and instant payouts.

Stablecoins like USDT and USDC are increasingly popular for gambling because they eliminate price volatility. Many Ethereum casinos now accept ERC-20 stablecoins alongside native ETH. If you want to gamble with crypto but hate the price swings, use a stablecoin. The casino experience is the same, but your bankroll stays constant in fiat terms.

How to Choose an Ethereum Casino: A Practical Checklist

Before you deposit, run through this mental checklist:

  • Is the licence verifiable? Search the regulator’s database.
  • Do they have a named operator and a physical address?
  • Are withdrawal times stated clearly? Avoid sites that say ‘within 72 hours’ for crypto.
  • What are the wagering requirements on the bonus? Above 40x is a no.
  • Is customer support reachable? Send a test message before depositing.
  • Does the casino have a responsible gambling page with real tools, or just a link to GamCare?

If any of these fail, walk away. There are enough Ethereum casinos that you do not need to tolerate shady terms.

Is Ethereum gambling legal in the UK?

Playing at an offshore Ethereum casino from the UK is not a criminal offence for the player, but the casino itself is not licensed by the UK Gambling Commission. You lose UK consumer protections, so choose operators with long histories and transparent licensing.

How fast are Ethereum casino withdrawals?

After the casino approves a withdrawal, Ethereum transactions usually arrive in your wallet within 10 to 60 minutes, depending on network congestion. The casino’s internal approval process is the bottleneck; some approve instantly, others take a few hours.

Do I need a wallet to play at Ethereum casinos?

Yes. You need a personal Ethereum wallet, such as MetaMask, Trust Wallet, or a hardware wallet. You send ETH from that wallet to the casino’s deposit address. The casino never needs your private keys, and you should never share them.

Are Ethereum casino bonuses worth it?

Most welcome bonuses and free spins carry wagering requirements of 35x or higher, which makes them mathematically negative. Cashback and rakeback offers are usually better because they come with fewer strings. Always read the terms before claiming.

Can I use MetaMask at Ethereum casinos?

Some Ethereum casinos support direct MetaMask connections for deposits and withdrawals, but most simply provide a deposit address. Direct wallet connection is convenient but increases smart contract exposure. For casual play, a standard address transfer is safer.

What is the minimum deposit at Ethereum casinos?

Most offshore Ethereum casinos accept deposits from around 0.001 ETH, but the exact amount varies with network fees and the operator’s policy. Always check the cashier page because some platforms adjust minimums in real time.

Ethereum casinos are not for everyone. If you want UKGC oversight and the ability to complain to an ombudsman, stick with licensed UK operators and debit cards. But if you accept the trade-off of offshore risk for faster payments and crypto privacy, the platforms in this guide are a reasonable place to start. Deposit small, verify withdrawals early, and never treat a casino bonus as anything other than a retention device.

Casinos That Accept Ethereum UK 2026: Top Ethereum Casino Sites Compared